Variables

  • ID: identificación de vuelo.
  • DATOP: Fecha de vuelo.
  • FLTID: Número de vuelo
  • DEPSTN: Punto de partida
  • ARRSTN: Punto de llegada
  • STD: Hora de salida programada
  • STA: Hora prevista de llegada
  • STATUS: Estado del vuelo
  • AC: Código de aeronave
  • target: Tiempo de retraso

Train y Test

  • Train:
    • En el conjunto de datos de train hay 107833 vuelos registrados.
    • El rango de fechas de vuelos es desde el 01-01-2016 hasta 31-12-2018.
    • Hay 1861 vuelos registrados. Cada vuelo puede estar registrado en diferentes fechas.
    • En total son 132 puntos de destino diferentes y 128 puntos de llegada.
    • Hay en total 5 estados (STATUS) de vuelo.
    • Hay 68 códigos de aeronaves diferentes.
    • El tiempo de retraso (target) está dado en minutos.
  • Test:
    • En el conjunto de test hay 9333 vuelos registrados.
    • El rango de fechas de vuelos es desde 01-05-2016 hasta 20-09-2018.
    • Hay 700 vuelos registrados.
    • En total son 82 puntos de destino y 84 puntos de llegada diferentes.
    • Los mismos 5 estados de vuelo.
    • Hay 44 códigos de aeronaves.

Conteos

  • ¿Cuántos vuelos (FLTID) del test coinciden con el train?:

FALSE  TRUE 
   67  9266 
  • ¿Cuántos puntos de destino coinciden entre el test y train?:

FALSE  TRUE 
    3  9330 
  • ¿Cuántos puntos de llegada coinciden entre el test y train?:

FALSE  TRUE 
    3  9330 
  • ¿Cuántos códigos de aeronave coinciden entre el test y train?:

FALSE  TRUE 
   21  9312 

Nuevas variables

  • Basado en las variables originales derivo las siguientes características:
    • Días del mes, días de la semana, meses y año de vuelo.
    • Agrego trimestres.
    • Agrego semana del año.
    • Agrego una variable que indique si es fin de semana.
    • Agrego una nueva variable binaria que indique si es fin de mes. Si la fecha del vuelo está entre el 28 a 31 de cada mes, lo categorizo como fin de mes.
    • Agrego una nueva variable binaria con fechas especiales. Aunque son muchas las fechas especiales que podrían ser tenidas en cuenta, incluyo el día del trabajo (1 de mayo), el día de san valentín (19 de septiembre), el día internacional de la mujer (8 de marzo), año nuevo (01 de enero), día de los reyes magos (06 de enero), noche de Hallowen (31 de octubre), noche buena (24 de diciembre), día de navidad (25 de diciembre) y fin de año (31 de diciembre).
    • Con el tiempo de salida obtengo la hora y establezco una nueva variable que define si el vuelo es en la madrugada, mañana, tarde o noche.
    • Resto el tiempo de salida con el tiempo de llegada para conocer el tiempo promedio de vuelo. El resultado estará dado en minutos, sin embargo, lo convierto a horas dividiendo sobre 60. Hay vuelos con inconsistencias en los tiempos de partida y llegada, por tal motivo el tiempo de vuelo de aquellos que superan las 24 horas les agrego NA (363 registros en total).
    • Con el tiempo promedio de vuelo clasifico los vuelos en vuelo corto (hasta 2 horas), vuelo moderado (entre 2 y 5 horas) y vuelo largo (mayor a 5 horas).
    • Cuento el número de vuelos por punto de salida. Esto supongo que servirá para observar la demanda de cada sitio (ciudad o país), esperando que donde haya más demanda posiblemente haya menor capacidad de reacción (¿o al contrario?) y quizás mayores retrasos. Lo mismo hago por punto de llegada.
    • Cuento número de vuelos por código de aeronave, en el mismo orden de ideas de oferta-demanda.
    • Opcionales: estas variables las agrego como opcionales porque podrían causar sobreajuste en los modelos.
      • Para cada vuelo -FLTID promedio el tiempo de retraso. Los vuelos que están en el train que no pertenecen al test les agrego NA. También se podría calcular la mediana en lugar del promedio.
      • Para cada vuelo obtengo la desviación estándar de la variable objetivo.
      • Para cada vuelo obtengo el mínimo y máximo tiempo de retraso.
      • Calculo el rango intercuartílico de retraso de cada vuelo.

Nuevas Train 1

Nuevas Test 1

Nuevas Train 2

# Fechas especiales
fechas <- c("5_1", "9_19", "3_8", "1_1", "1_6", "10_31", "12_24", "12_25",
            "12_31")

# Nuevas variables train
dataTrain %>% 
  mutate(dayWeek = weekdays(DATOP),
         mes = factor(month(DATOP)),
         dia = mday(DATOP),
         anio = factor(year(DATOP)),
         trimestre = factor(quarters(DATOP)),
         weekYear = week(DATOP),
         endWeek = factor(if_else(dayWeek %in% c("sábado", "domingo"),
                           true = "Si", false = "No")),
         finMes = if_else(dia %in% c(28, 29, 30, 31),
                          true = "Si", false = "No"),
         horaVuelo = hour(STD),
         horaVueloClas = if_else(
           horaVuelo >= 0 & horaVuelo < 6,
           true = "Magrudada",
           false = if_else(
             horaVuelo >= 6 & horaVuelo < 12,
             true = "Mañana",
             false = if_else(
               horaVuelo >= 12 & horaVuelo < 18,
               true = "Tarde",
               false = "Noche"))),
         tiempoVuelo = as.numeric(STA - STD)/60,
         tiempoVuelo = ifelse(tiempoVuelo > 24, NA, tiempoVuelo),
         tiempoClas = if_else(tiempoVuelo <= 2,
                              true = "Corto",
                              false = if_else(
                                tiempoVuelo > 5,
                                true = "Largo",
                                false = "Moderado"))) %>% 
  group_by(DEPSTN) %>% 
  mutate(vuelosPartida = n()) %>% 
  ungroup() %>% 
  group_by(ARRSTN) %>% 
  mutate(vuelosDestino = n()) %>% 
  ungroup() %>% 
  group_by(AC) %>% 
  mutate(vuelosAC = n()) %>% 
  group_by(FLTID) %>% 
  mutate(promedioRetraso = mean(target, na.rm = TRUE),
         medianaRetraso = median(target, na.rm = TRUE),
         desvRetraso = sd(target, na.rm = TRUE),
         minRetraso = min(target, na.rm = TRUE),
         maxRetraso = max(target, na.rm = TRUE),
         RicRetraso = IQR(target, na.rm = TRUE)) %>% 
  ungroup() %>% 
  unite(mes, dia, col = "diaMes", remove = FALSE) %>% 
  mutate(fechaEspecial = if_else(diaMes %in% fechas,
                                 true = "Si", false = "No")) %>% 
  mutate_if(is.character, as.factor) %>% 
  select(target, everything()) %>% 
  select(-diaMes) ->
  newDataTrain
  
newDataTrain

Nuevas Test 2

Modelos

Modelo 1

# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA))

# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.70,
                            list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
                              label = dfTrain[, 1],
                              categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
                             label = dfTest[, 1],
                             categorical_feature = catFeatures)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.1,
  feature_fraction = 1,
  bagging_fraction = 1,
  max_depth = -1
)

# Train model
modelo <- lgb.train(params = myParams,
                    data = dataTrain_lgbm,
                    nrounds = 10000,
                    valids = list(test = dataTest_lgbm),
                    early_stopping_rounds = 500)

#best iter: 238
#best score: 107.9903

# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
                                              select(-c(ID, DATOP, STD, STA))), 
                        num_iteration = modelo$best_iter)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR1

# Export submission for zindi
write.csv(lgbmR1, file = "../submission/lgbmR1Vuelos.csv", row.names = FALSE)

Modelo 2

# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA))

# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.70,
                            list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
                              label = dfTrain[, 1],
                              categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
                             label = dfTest[, 1],
                             categorical_feature = catFeatures)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.1,
  feature_fraction = 0.5,
  bagging_fraction = 1,
  min_data_in_leaf = 100,
  num_leaves = 255,
  max_depth = -1
)

# Train model
modelo <- lgb.train(params = myParams,
                    data = dataTrain_lgbm,
                    nrounds = 10000,
                    valids = list(test = dataTest_lgbm),
                    early_stopping_rounds = 500)

#best iter: 69
#best score: 109.0651

# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
                                              select(-c(ID, DATOP, STD, STA))), 
                        num_iteration = modelo$best_iter)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR2

# Export submission for zindi
write.csv(lgbmR2, file = "../submission/lgbmR2Vuelos.csv", row.names = FALSE)

Modelo 3

# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA))

# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
                            list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
                              label = dfTrain[, 1],
                              categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
                             label = dfTest[, 1],
                             categorical_feature = catFeatures)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.01,
  feature_fraction = 1,
  bagging_fraction = 1,
  max_depth = -1
)

# Train model
modelo <- lgb.train(params = myParams,
                    data = dataTrain_lgbm,
                    nrounds = 10000,
                    valids = list(test = dataTest_lgbm),
                    early_stopping_rounds = 500)

#best iter: 1426
#best score: 106.8384

# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
                                              select(-c(ID, DATOP, STD, STA))), 
                        num_iteration = modelo$best_iter)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR3

# Export submission for zindi
write.csv(lgbmR3, file = "../submission/lgbmR3Vuelos.csv", row.names = FALSE)

Modelo 4

# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA))

# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
                            list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
                              label = dfTrain[, 1],
                              categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
                             label = dfTest[, 1],
                             categorical_feature = catFeatures)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.01,
  feature_fraction = 0.5,
  bagging_fraction = 1,
  min_data_in_leaf = 100,
  num_leaves = 255,
  max_depth = -1
)

# Train model
modelo <- lgb.train(params = myParams,
                    data = dataTrain_lgbm,
                    nrounds = 10000,
                    valids = list(test = dataTest_lgbm),
                    early_stopping_rounds = 500)

#best iter: 878
#best score: 106.9247

# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
                                              select(-c(ID, DATOP, STD, STA))), 
                        num_iteration = modelo$best_iter)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR4

# Export submission for zindi
write.csv(lgbmR4, file = "../submission/lgbmR4Vuelos.csv", row.names = FALSE)

Modelo 5

# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA))

# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
                            list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
                              label = dfTrain[, 1],
                              categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
                             label = dfTest[, 1],
                             categorical_feature = catFeatures)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.01,
  feature_fraction = 1,
  bagging_fraction = 1,
  min_data_in_leaf = 300,
  num_leaves = 500,
  max_bin = 500,
  max_depth = -1
)

# Train model
modelo <- lgb.train(params = myParams,
                    data = dataTrain_lgbm,
                    nrounds = 10000,
                    valids = list(test = dataTest_lgbm),
                    early_stopping_rounds = 500)

#best iter: 689
#best score: 108.1176

# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
                                              select(-c(ID, DATOP, STD, STA))), 
                        num_iteration = modelo$best_iter)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR5

# Export submission for zindi
write.csv(lgbmR5, file = "../submission/lgbmR5Vuelos.csv", row.names = FALSE)

Modelo 6

# Load data
load("../myData/Train2.Rdata")
load("../myData/Test2.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA, minRetraso, maxRetraso))

# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
                            list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
                              label = dfTrain[, 1],
                              categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
                             label = dfTest[, 1],
                             categorical_feature = catFeatures)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.01,
  feature_fraction = 1,
  bagging_fraction = 1,
  max_depth = -1
)

# Train model
modelo <- lgb.train(params = myParams,
                    data = dataTrain_lgbm,
                    nrounds = 10000,
                    valids = list(test = dataTest_lgbm),
                    early_stopping_rounds = 500)

#best iter: 1620
#best score: 105.6486

# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
                                              select(-c(ID, DATOP, STD, STA,
                                                        minRetraso, maxRetraso))), 
                        num_iteration = modelo$best_iter)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR6

# Export submission for zindi
write.csv(lgbmR6, file = "../submission/lgbmR6Vuelos.csv", row.names = FALSE)

Modelo 7

# Load data
load("../myData/Train2.Rdata")
load("../myData/Test2.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA, dia, minRetraso, maxRetraso, RicRetraso, fechaEspecial))

# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
                            list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
                              label = dfTrain[, 1],
                              categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
                             label = dfTest[, 1],
                             categorical_feature = catFeatures)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.01,
  feature_fraction = 1,
  bagging_fraction = 1,
  max_depth = -1
)

# Train model
modelo <- lgb.train(params = myParams,
                    data = dataTrain_lgbm,
                    nrounds = 10000,
                    valids = list(test = dataTest_lgbm),
                    early_stopping_rounds = 500)

#best iter: 1511
#best score: 106.6549

# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
                                              select(-c(ID, DATOP, STD, STA,
                                                        dia, minRetraso, maxRetraso, RicRetraso,
                                                        fechaEspecial))), 
                        num_iteration = modelo$best_iter)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR7

# Export submission for zindi
write.csv(lgbmR7, file = "../submission/lgbmR7Vuelos.csv", row.names = FALSE)

Modelo 8

# Load data
load("../myData/Train2.Rdata")
load("../myData/Test2.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA, fechaEspecial))

# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
                            list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
                              label = dfTrain[, 1],
                              categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
                             label = dfTest[, 1],
                             categorical_feature = catFeatures)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.01,
  feature_fraction = 1,
  bagging_fraction = 1,
  max_depth = -1
)

# Train model
modelo <- lgb.train(params = myParams,
                    data = dataTrain_lgbm,
                    nrounds = 10000,
                    valids = list(test = dataTest_lgbm),
                    early_stopping_rounds = 500)

#best iter: 1504
#best score: 105.7866

# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
                                              select(-c(ID, DATOP, STD, STA, fechaEspecial))), 
                        num_iteration = modelo$best_iter)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR8

# Export submission for zindi
write.csv(lgbmR8, file = "../submission/lgbmR8Vuelos.csv", row.names = FALSE)

Modelo 9

# Load data
load("../myData/Train2.Rdata")
load("../myData/Test2.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA, fechaEspecial))

# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.90,
                            list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
                              label = dfTrain[, 1],
                              categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
                             label = dfTest[, 1],
                             categorical_feature = catFeatures)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.01,
  feature_fraction = 1,
  bagging_fraction = 1,
  max_depth = -1
)

# Train model
modelo <- lgb.train(params = myParams,
                    data = dataTrain_lgbm,
                    nrounds = 30000,
                    valids = list(test = dataTest_lgbm),
                    early_stopping_rounds = 500)

#best iter: 19519
#best score: 102.1988

# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
                                              select(-c(ID, DATOP, STD, STA, fechaEspecial))), 
                        num_iteration = modelo$best_iter)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR9

# Export submission for zindi
write.csv(lgbmR9, file = "../submission/lgbmR9Vuelos.csv",
          row.names = FALSE)

Modelo 10

# Mod1 + KFold
# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA))

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# caret for partition data with resample
library(caret)
set.seed(123)
index <- createDataPartition(y = dataTrain$target, times = 10, p = 0.70,
                             list = TRUE)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.1,
  feature_fraction = 1,
  bagging_fraction = 1,
  max_depth = -1
)

# lightgbm with K-Fold manually (k = 10)
library(lightgbm)
k <- 10
predTest <- list()
bestScore <- c()
for (i in 1:k) {
  
  # Data train and test
  dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[index[[i]], -1]),
                                label = dataTrain[index[[i]], 1],
                                categorical_feature = catFeatures)
  dataTest_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[-index[[i]], -1]),
                               label = dataTrain[-index[[i]], 1],
                               categorical_feature = catFeatures)
  
  # Train model
  model <- lgb.train(params = myParams,
                     data = dataTrain_lgbm,
                     nrounds = 10000,
                     valids = list(test = dataTest_lgbm),
                     early_stopping_rounds = 500)
  
  # Predictions
  predictions <- predict(model,
                         data.matrix(newDataTest %>%
                                       select(-c(ID, DATOP, STD, STA))),
                         num_iteration = model$best_iter)
  predTest[[i]] = predictions
  
  # Best score for iteration
  bestScore[i] = model$best_score
  
  # Next iteration
  cat("Iteration:==========", i, "RSME:==========", model$best_score, "Ready!")
}

# Mean predictions
dataPred <- as.data.frame(predTest)
names(dataPred) <- paste0("Mod", 1:10)
predicciones <- apply(dataPred, 1, mean)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR10

# Export submission for zindi
write.csv(lgbmR10, file = "../submission/lgbmR10Vuelos.csv", row.names = FALSE)

Modelo 11

# Mod2 + KFold
# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA))

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# caret for partition data with resample
library(caret)
set.seed(123)
index <- createDataPartition(y = dataTrain$target, times = 10, p = 0.70,
                             list = TRUE)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.1,
  feature_fraction = 0.5,
  bagging_fraction = 1,
  min_data_in_leaf = 100,
  num_leaves = 255,
  max_depth = -1
)

# lightgbm with K-Fold manually (k = 10)
library(lightgbm)
k <- 10
predTest <- list()
bestScore <- c()
for (i in 1:k) {
  
  # Data train and test
  dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[index[[i]], -1]),
                                label = dataTrain[index[[i]], 1],
                                categorical_feature = catFeatures)
  dataTest_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[-index[[i]], -1]),
                               label = dataTrain[-index[[i]], 1],
                               categorical_feature = catFeatures)
  
  # Train model
  model <- lgb.train(params = myParams,
                     data = dataTrain_lgbm,
                     nrounds = 10000,
                     valids = list(test = dataTest_lgbm),
                     early_stopping_rounds = 500)
  
  # Predictions
  predictions <- predict(model,
                         data.matrix(newDataTest %>%
                                       select(-c(ID, DATOP, STD, STA))),
                         num_iteration = model$best_iter)
  predTest[[i]] = predictions
  
  # Best score for iteration
  bestScore[i] = model$best_score
  
  # Next iteration
  cat("Iteration:==========", i, "RSME:==========", model$best_score, "Ready!")
}

# Mean predictions
dataPred <- as.data.frame(predTest)
names(dataPred) <- paste0("Mod", 1:10)
predicciones <- apply(dataPred, 1, mean)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR11

# Export submission for zindi
write.csv(lgbmR11, file = "../submission/lgbmR11Vuelos.csv", row.names = FALSE)

Modelo 12

# Mod1 + KFold
# Load data
load("../myData/Train2.Rdata")
load("../myData/Test2.Rdata")
dataSample <- data.table::fread("../data/sample.csv")

newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)

# Selection of variables for analysis 
library(tidyverse)
dataTrain <- newDataTrain %>%
  select(-c(ID, DATOP, STD, STA))

# Categorical features
catFeatures <- names(
  dataTrain %>% select_if(is.factor)
)

# caret for partition data with resample
library(caret)
set.seed(123)
index <- createDataPartition(y = dataTrain$target, times = 10, p = 0.70,
                             list = TRUE)

# Parameters for lightgbm
myParams <- list(
  boosting = "gbdt",
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.01,
  feature_fraction = 1,
  bagging_fraction = 1,
  max_depth = -1
)

# lightgbm with K-Fold manually (k = 10)
library(lightgbm)
k <- 10
predTest <- list()
bestScore <- c()
for (i in 1:k) {
  
  # Data train and test
  dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[index[[i]], -1]),
                                label = dataTrain[index[[i]], 1],
                                categorical_feature = catFeatures)
  dataTest_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[-index[[i]], -1]),
                               label = dataTrain[-index[[i]], 1],
                               categorical_feature = catFeatures)
  
  # Train model
  model <- lgb.train(params = myParams,
                     data = dataTrain_lgbm,
                     nrounds = 10000,
                     valids = list(test = dataTest_lgbm),
                     early_stopping_rounds = 500)
  
  # Predictions
  predictions <- predict(model,
                         data.matrix(newDataTest %>%
                                       select(-c(ID, DATOP, STD, STA))),
                         num_iteration = model$best_iter)
  predTest[[i]] = predictions
  
  # Best score for iteration
  bestScore[i] = model$best_score
  
  # Next iteration
  cat("Iteration:==========", i, "RSME:==========", model$best_score, "Ready!")
}

# Mean predictions
dataPred <- as.data.frame(predTest)
names(dataPred) <- paste0("Mod", 1:10)
predicciones <- apply(dataPred, 1, mean)

predicciones[predicciones < 0] <- 0
x11();hist(predicciones)

# Submission
dataSample %>% 
  select(ID) %>% 
  mutate(target = predicciones) ->
  lgbmR12

# Export submission for zindi
write.csv(lgbmR12, file = "../submission/lgbmR12Vuelos.csv", row.names = FALSE)
LS0tDQp0aXRsZTogIlJldHJhc29zIGRlIHZ1ZWxvcyINCnN1YnRpdGxlOiAiQW7DoWxpc2lzIEV4cGxvcmF0b3JpbyINCmF1dGhvcjogIkVkaW1lciBEYXZpZCBKYXJhbWlsbG8gKCpzaWRlcmV1cyopIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNzczogZXN0aWxvLmNzcw0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlDQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgaGlnaGxpZ2h0OiBicmVlemVkYXJrDQogICAgDQotLS0NCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgZXZhbCA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gImNlbnRlciIsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gOSwNCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFKQ0KYGBgDQoNCjxjZW50ZXI+DQo8aW1nIHNyYyA9ICJpbWcvaW1nMS5wbmciIC8+DQo8L2NlbnRlcj4NCg0KIyBSZXRvDQoNCi0gW1JldG8gZW4gRGF0YVNvdXJjZS5haV0oaHR0cHM6Ly93d3cuZGF0YXNvdXJjZS5haS9lcy9ob21lL2NvbXBldGl0aW9ucy9wcmVkaWNjaW9uLWRlLXJldHJhc29zLWRlLXZ1ZWxvcy1wYXJhLXVuYS1hZXJvbGluZWEpDQoNCiMgRGF0b3MNCg0KYGBge3J9DQpsaWJyYXJ5KGRhdGEudGFibGUpDQpkYXRhVHJhaW4gPC0gZnJlYWQoIi4uL2RhdGEvdHJhaW4uY3N2IikNCmRhdGFUZXN0IDwtIGZyZWFkKCIuLi9kYXRhL3Rlc3QuY3N2IikNCmRhdGFTYW1wbGUgPC0gZnJlYWQoIi4uL2RhdGEvc2FtcGxlLmNzdiIpDQpgYGANCg0KIyBWYXJpYWJsZXMNCg0KLSAqKklEOioqIGlkZW50aWZpY2FjacOzbiBkZSB2dWVsby4NCi0gKipEQVRPUDoqKiBGZWNoYSBkZSB2dWVsby4NCi0gKipGTFRJRDoqKiBOw7ptZXJvIGRlIHZ1ZWxvDQotICoqREVQU1ROOioqIFB1bnRvIGRlIHBhcnRpZGENCi0gKipBUlJTVE46KiogUHVudG8gZGUgbGxlZ2FkYQ0KLSAqKlNURDoqKiBIb3JhIGRlIHNhbGlkYSBwcm9ncmFtYWRhDQotICoqU1RBOioqIEhvcmEgcHJldmlzdGEgZGUgbGxlZ2FkYQ0KLSAqKlNUQVRVUzoqKiBFc3RhZG8gZGVsIHZ1ZWxvDQotICoqQUM6KiogQ8OzZGlnbyBkZSBhZXJvbmF2ZQ0KLSAqKnRhcmdldDoqKiBUaWVtcG8gZGUgcmV0cmFzbw0KDQojIFRyYWluIHkgVGVzdA0KDQotICoqVHJhaW46KioNCiAgLSBFbiBlbCBjb25qdW50byBkZSBkYXRvcyBkZSAqdHJhaW4qIGhheSAxMDc4MzMgdnVlbG9zIHJlZ2lzdHJhZG9zLg0KICAtIEVsIHJhbmdvIGRlIGZlY2hhcyBkZSB2dWVsb3MgZXMgZGVzZGUgZWwgMDEtMDEtMjAxNiBoYXN0YSAzMS0xMi0yMDE4Lg0KICAtIEhheSAxODYxIHZ1ZWxvcyByZWdpc3RyYWRvcy4gQ2FkYSB2dWVsbyBwdWVkZSBlc3RhciByZWdpc3RyYWRvIGVuIGRpZmVyZW50ZXMgZmVjaGFzLg0KICAtIEVuIHRvdGFsIHNvbiAxMzIgcHVudG9zIGRlIGRlc3Rpbm8gZGlmZXJlbnRlcyB5IDEyOCBwdW50b3MgZGUgbGxlZ2FkYS4NCiAgLSBIYXkgZW4gdG90YWwgNSBlc3RhZG9zICgqU1RBVFVTKikgZGUgdnVlbG8uDQogIC0gSGF5IDY4IGPDs2RpZ29zIGRlIGFlcm9uYXZlcyBkaWZlcmVudGVzLg0KICAtIEVsIHRpZW1wbyBkZSByZXRyYXNvICgqdGFyZ2V0KikgZXN0w6EgZGFkbyBlbiBtaW51dG9zLg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KIyBUcmFpbg0KZGF0YVRyYWluICU+JSANCiAgbXV0YXRlKERBVE9QID0gYXMuRGF0ZShEQVRPUCksDQogICAgICAgICBGTFRJRCA9IGFzLmZhY3RvcihGTFRJRCksDQogICAgICAgICBERVBTVE4gPSBhcy5mYWN0b3IoREVQU1ROKSwNCiAgICAgICAgIEFSUlNUTiA9IGFzLmZhY3RvcihBUlJTVE4pLA0KICAgICAgICAgU1REID0gYXMuUE9TSVhjdChTVEQpLA0KICAgICAgICAgU1RBID0gZ3N1YigiXFwuIiwgIjoiLCBTVEEpLA0KICAgICAgICAgU1RBID0gYXMuUE9TSVhjdChTVEEpLA0KICAgICAgICAgU1RBVFVTID0gYXMuZmFjdG9yKFNUQVRVUyksDQogICAgICAgICBBQyA9IGFzLmZhY3RvcihBQykpIC0+DQogIGRhdGFUcmFpbg0KaGVhZChkYXRhVHJhaW4sIG4gPSAxMEwpDQpgYGANCg0KLSAqKlRlc3Q6KioNCiAgLSBFbiBlbCBjb25qdW50byBkZSAqdGVzdCogaGF5IDkzMzMgdnVlbG9zIHJlZ2lzdHJhZG9zLg0KICAtIEVsIHJhbmdvIGRlIGZlY2hhcyBkZSB2dWVsb3MgZXMgZGVzZGUgMDEtMDUtMjAxNiBoYXN0YSAyMC0wOS0yMDE4Lg0KICAtIEhheSA3MDAgdnVlbG9zIHJlZ2lzdHJhZG9zLg0KICAtIEVuIHRvdGFsIHNvbiA4MiBwdW50b3MgZGUgZGVzdGlubyB5IDg0IHB1bnRvcyBkZSBsbGVnYWRhIGRpZmVyZW50ZXMuDQogIC0gTG9zIG1pc21vcyA1IGVzdGFkb3MgZGUgdnVlbG8uDQogIC0gSGF5IDQ0IGPDs2RpZ29zIGRlIGFlcm9uYXZlcy4NCg0KYGBge3J9DQojIFRlc3QNCmRhdGFUZXN0ICU+JSANCiAgbXV0YXRlKERBVE9QID0gYXMuRGF0ZShEQVRPUCksDQogICAgICAgICBGTFRJRCA9IGFzLmZhY3RvcihGTFRJRCksDQogICAgICAgICBERVBTVE4gPSBhcy5mYWN0b3IoREVQU1ROKSwNCiAgICAgICAgIEFSUlNUTiA9IGFzLmZhY3RvcihBUlJTVE4pLA0KICAgICAgICAgU1REID0gYXMuUE9TSVhjdChTVEQpLA0KICAgICAgICAgU1RBID0gZ3N1YigiXFwuIiwgIjoiLCBTVEEpLA0KICAgICAgICAgU1RBID0gYXMuUE9TSVhjdChTVEEpLA0KICAgICAgICAgU1RBVFVTID0gYXMuZmFjdG9yKFNUQVRVUyksDQogICAgICAgICBBQyA9IGFzLmZhY3RvcihBQykpIC0+DQogIGRhdGFUZXN0DQpoZWFkKGRhdGFUZXN0LCBuID0gMTBMKQ0KYGBgDQoNCiMgQ29udGVvcw0KDQotICoqwr9DdcOhbnRvcyB2dWVsb3MgKCpGTFRJRCopIGRlbCB0ZXN0IGNvaW5jaWRlbiBjb24gZWwgdHJhaW4/OioqDQoNCmBgYHtyfQ0KdGFibGUoZGF0YVRlc3QkRkxUSUQgJWluJSBkYXRhVHJhaW4kRkxUSUQpDQpgYGANCg0KLSAqKsK/Q3XDoW50b3MgcHVudG9zIGRlIGRlc3Rpbm8gY29pbmNpZGVuIGVudHJlIGVsIHRlc3QgeSB0cmFpbj86KioNCg0KYGBge3J9DQp0YWJsZShkYXRhVGVzdCRERVBTVE4gJWluJSBkYXRhVHJhaW4kREVQU1ROKQ0KYGBgDQoNCi0gKirCv0N1w6FudG9zIHB1bnRvcyBkZSBsbGVnYWRhIGNvaW5jaWRlbiBlbnRyZSBlbCB0ZXN0IHkgdHJhaW4/OioqDQoNCmBgYHtyfQ0KdGFibGUoZGF0YVRlc3QkQVJSU1ROICVpbiUgZGF0YVRyYWluJEFSUlNUTikNCmBgYA0KDQotICoqwr9DdcOhbnRvcyBjw7NkaWdvcyBkZSBhZXJvbmF2ZSBjb2luY2lkZW4gZW50cmUgZWwgdGVzdCB5IHRyYWluPzoqKg0KDQpgYGB7cn0NCnRhYmxlKGRhdGFUZXN0JEFDICVpbiUgZGF0YVRyYWluJEFDKQ0KYGBgDQoNCiMgTnVldmFzIHZhcmlhYmxlcw0KDQotIEJhc2FkbyBlbiBsYXMgdmFyaWFibGVzIG9yaWdpbmFsZXMgZGVyaXZvIGxhcyBzaWd1aWVudGVzIGNhcmFjdGVyw61zdGljYXM6DQogIC0gRMOtYXMgZGVsIG1lcywgZMOtYXMgZGUgbGEgc2VtYW5hLCBtZXNlcyB5IGHDsW8gZGUgdnVlbG8uDQogIC0gQWdyZWdvIHRyaW1lc3RyZXMuDQogIC0gQWdyZWdvIHNlbWFuYSBkZWwgYcOxby4NCiAgLSBBZ3JlZ28gdW5hIHZhcmlhYmxlIHF1ZSBpbmRpcXVlIHNpIGVzIGZpbiBkZSBzZW1hbmEuDQogIC0gQWdyZWdvIHVuYSBudWV2YSB2YXJpYWJsZSBiaW5hcmlhIHF1ZSBpbmRpcXVlIHNpIGVzIGZpbiBkZSBtZXMuIFNpIGxhIGZlY2hhIGRlbCB2dWVsbyBlc3TDoSBlbnRyZSBlbCAyOCBhIDMxIGRlIGNhZGEgbWVzLCBsbyBjYXRlZ29yaXpvIGNvbW8gZmluIGRlIG1lcy4NCiAgLSBBZ3JlZ28gdW5hIG51ZXZhIHZhcmlhYmxlIGJpbmFyaWEgY29uIGZlY2hhcyBlc3BlY2lhbGVzLiBBdW5xdWUgc29uIG11Y2hhcyBsYXMgZmVjaGFzIGVzcGVjaWFsZXMgcXVlIHBvZHLDrWFuIHNlciB0ZW5pZGFzIGVuIGN1ZW50YSwgaW5jbHV5byBlbCBkw61hIGRlbCB0cmFiYWpvICgxIGRlIG1heW8pLCBlbCBkw61hIGRlIHNhbiB2YWxlbnTDrW4gKDE5IGRlIHNlcHRpZW1icmUpLCBlbCBkw61hIGludGVybmFjaW9uYWwgZGUgbGEgbXVqZXIgKDggZGUgbWFyem8pLCBhw7FvIG51ZXZvICgwMSBkZSBlbmVybyksIGTDrWEgZGUgbG9zIHJleWVzIG1hZ29zICgwNiBkZSBlbmVybyksIG5vY2hlIGRlIEhhbGxvd2VuICgzMSBkZSBvY3R1YnJlKSwgbm9jaGUgYnVlbmEgKDI0IGRlIGRpY2llbWJyZSksIGTDrWEgZGUgbmF2aWRhZCAoMjUgZGUgZGljaWVtYnJlKSB5IGZpbiBkZSBhw7FvICgzMSBkZSBkaWNpZW1icmUpLg0KICAtIENvbiBlbCB0aWVtcG8gZGUgc2FsaWRhIG9idGVuZ28gbGEgKipob3JhKiogeSBlc3RhYmxlemNvIHVuYSBudWV2YSB2YXJpYWJsZSBxdWUgZGVmaW5lIHNpIGVsIHZ1ZWxvIGVzIGVuIGxhICptYWRydWdhZGEqLCAqbWHDsWFuYSosICp0YXJkZSogbyAqbm9jaGUqLg0KICAtIFJlc3RvIGVsIHRpZW1wbyBkZSBzYWxpZGEgY29uIGVsIHRpZW1wbyBkZSBsbGVnYWRhIHBhcmEgY29ub2NlciBlbCB0aWVtcG8gcHJvbWVkaW8gZGUgdnVlbG8uIEVsIHJlc3VsdGFkbyBlc3RhcsOhIGRhZG8gZW4gbWludXRvcywgc2luIGVtYmFyZ28sIGxvIGNvbnZpZXJ0byBhIGhvcmFzIGRpdmlkaWVuZG8gc29icmUgNjAuIDx0cmVkPkhheSB2dWVsb3MgY29uIGluY29uc2lzdGVuY2lhcyBlbiBsb3MgdGllbXBvcyBkZSBwYXJ0aWRhIHkgbGxlZ2FkYSwgcG9yIHRhbCBtb3Rpdm8gZWwgdGllbXBvIGRlIHZ1ZWxvIGRlIGFxdWVsbG9zIHF1ZSBzdXBlcmFuIGxhcyAyNCBob3JhcyBsZXMgYWdyZWdvIE5BICgzNjMgcmVnaXN0cm9zIGVuIHRvdGFsKS48L3RyZWQ+DQogIC0gQ29uIGVsIHRpZW1wbyBwcm9tZWRpbyBkZSB2dWVsbyBjbGFzaWZpY28gbG9zIHZ1ZWxvcyBlbiAqdnVlbG8gY29ydG8qIChoYXN0YSAyIGhvcmFzKSwgKnZ1ZWxvIG1vZGVyYWRvKiAoZW50cmUgMiB5IDUgaG9yYXMpIHkgKnZ1ZWxvIGxhcmdvKiAobWF5b3IgYSA1IGhvcmFzKS4NCiAgLSBDdWVudG8gZWwgbsO6bWVybyBkZSB2dWVsb3MgcG9yIHB1bnRvIGRlIHNhbGlkYS4gRXN0byBzdXBvbmdvIHF1ZSBzZXJ2aXLDoSBwYXJhIG9ic2VydmFyIGxhIGRlbWFuZGEgZGUgY2FkYSBzaXRpbyAoY2l1ZGFkIG8gcGHDrXMpLCBlc3BlcmFuZG8gcXVlIGRvbmRlIGhheWEgbcOhcyBkZW1hbmRhIHBvc2libGVtZW50ZSBoYXlhIG1lbm9yIGNhcGFjaWRhZCBkZSByZWFjY2nDs24gKMK/byBhbCBjb250cmFyaW8/KSB5IHF1aXrDoXMgbWF5b3JlcyByZXRyYXNvcy4gTG8gbWlzbW8gaGFnbyBwb3IgcHVudG8gZGUgbGxlZ2FkYS4NCiAgLSBDdWVudG8gbsO6bWVybyBkZSB2dWVsb3MgcG9yIGPDs2RpZ28gZGUgYWVyb25hdmUsIGVuIGVsIG1pc21vIG9yZGVuIGRlIGlkZWFzIGRlICpvZmVydGEtZGVtYW5kYSouDQogIC0gKipPcGNpb25hbGVzOioqIGVzdGFzIHZhcmlhYmxlcyBsYXMgYWdyZWdvIGNvbW8gb3BjaW9uYWxlcyBwb3JxdWUgcG9kcsOtYW4gY2F1c2FyIHNvYnJlYWp1c3RlIGVuIGxvcyBtb2RlbG9zLg0KICAgIC0gUGFyYSBjYWRhIHZ1ZWxvICotRkxUSUQqIHByb21lZGlvIGVsIHRpZW1wbyBkZSByZXRyYXNvLiBMb3MgdnVlbG9zIHF1ZSBlc3TDoW4gZW4gZWwgKnRyYWluKiBxdWUgbm8gcGVydGVuZWNlbiBhbCAqdGVzdCogbGVzIGFncmVnbyBgTkFgLiBUYW1iacOpbiBzZSBwb2Ryw61hIGNhbGN1bGFyIGxhIG1lZGlhbmEgZW4gbHVnYXIgZGVsIHByb21lZGlvLg0KICAgIC0gUGFyYSBjYWRhIHZ1ZWxvIG9idGVuZ28gbGEgZGVzdmlhY2nDs24gZXN0w6FuZGFyIGRlIGxhIHZhcmlhYmxlIG9iamV0aXZvLg0KICAgIC0gUGFyYSBjYWRhIHZ1ZWxvIG9idGVuZ28gZWwgbcOtbmltbyB5IG3DoXhpbW8gdGllbXBvIGRlIHJldHJhc28uDQogICAgLSBDYWxjdWxvIGVsIHJhbmdvIGludGVyY3VhcnTDrWxpY28gZGUgcmV0cmFzbyBkZSBjYWRhIHZ1ZWxvLg0KDQojIyBOdWV2YXMgVHJhaW4gMQ0KDQpgYGB7cn0NCiMgTnVldmFzIHZhcmlhYmxlcyB0cmFpbg0KZGF0YVRyYWluICU+JSANCiAgbXV0YXRlKGRheVdlZWsgPSB3ZWVrZGF5cyhEQVRPUCksDQogICAgICAgICBtZXMgPSBmYWN0b3IobW9udGgoREFUT1ApKSwNCiAgICAgICAgIGFuaW8gPSBmYWN0b3IoeWVhcihEQVRPUCkpLA0KICAgICAgICAgdHJpbWVzdHJlID0gZmFjdG9yKHF1YXJ0ZXJzKERBVE9QKSksDQogICAgICAgICB3ZWVrWWVhciA9IHdlZWsoREFUT1ApLA0KICAgICAgICAgZW5kV2VlayA9IGZhY3RvcihpZl9lbHNlKGRheVdlZWsgJWluJSBjKCJzw6FiYWRvIiwgImRvbWluZ28iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpKSwNCiAgICAgICAgIGhvcmFWdWVsbyA9IGhvdXIoU1REKSwNCiAgICAgICAgIGhvcmFWdWVsb0NsYXMgPSBpZl9lbHNlKA0KICAgICAgICAgICBob3JhVnVlbG8gPj0gMCAmIGhvcmFWdWVsbyA8IDYsDQogICAgICAgICAgIHRydWUgPSAiTWFncnVkYWRhIiwNCiAgICAgICAgICAgZmFsc2UgPSBpZl9lbHNlKA0KICAgICAgICAgICAgIGhvcmFWdWVsbyA+PSA2ICYgaG9yYVZ1ZWxvIDwgMTIsDQogICAgICAgICAgICAgdHJ1ZSA9ICJNYcOxYW5hIiwNCiAgICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgICBob3JhVnVlbG8gPj0gMTIgJiBob3JhVnVlbG8gPCAxOCwNCiAgICAgICAgICAgICAgIHRydWUgPSAiVGFyZGUiLA0KICAgICAgICAgICAgICAgZmFsc2UgPSAiTm9jaGUiKSkpLA0KICAgICAgICAgdGllbXBvVnVlbG8gPSBhcy5udW1lcmljKFNUQSAtIFNURCkvNjAsDQogICAgICAgICB0aWVtcG9WdWVsbyA9IGlmZWxzZSh0aWVtcG9WdWVsbyA+IDI0LCBOQSwgdGllbXBvVnVlbG8pLA0KICAgICAgICAgdGllbXBvQ2xhcyA9IGlmX2Vsc2UodGllbXBvVnVlbG8gPD0gMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiQ29ydG8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2UgPSBpZl9lbHNlKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aWVtcG9WdWVsbyA+IDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiTGFyZ28iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSA9ICJNb2RlcmFkbyIpKSkgJT4lIA0KICBncm91cF9ieShERVBTVE4pICU+JSANCiAgbXV0YXRlKHZ1ZWxvc1BhcnRpZGEgPSBuKCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoQVJSU1ROKSAlPiUgDQogIG11dGF0ZSh2dWVsb3NEZXN0aW5vID0gbigpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGdyb3VwX2J5KEFDKSAlPiUgDQogIG11dGF0ZSh2dWVsb3NBQyA9IG4oKSkgJT4lIA0KICBncm91cF9ieShGTFRJRCkgJT4lIA0KICBtdXRhdGUocHJvbWVkaW9SZXRyYXNvID0gbWVhbih0YXJnZXQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICBtZWRpYW5hUmV0cmFzbyA9IG1lZGlhbih0YXJnZXQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICBkZXN2UmV0cmFzbyA9IHNkKHRhcmdldCwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGVfaWYoaXMuY2hhcmFjdGVyLCBhcy5mYWN0b3IpICU+JSANCiAgc2VsZWN0KHRhcmdldCwgZXZlcnl0aGluZygpKSAtPg0KICBuZXdEYXRhVHJhaW4NCm5ld0RhdGFUcmFpbg0KYGBgDQoNCiMjIE51ZXZhcyBUZXN0IDENCg0KYGBge3J9DQojIE51ZXZhcyB2YXJpYWJsZXMgdGVzdA0KZGF0YVRlc3QgJT4lIA0KICBtdXRhdGUoZGF5V2VlayA9IHdlZWtkYXlzKERBVE9QKSwNCiAgICAgICAgIG1lcyA9IGZhY3Rvcihtb250aChEQVRPUCkpLA0KICAgICAgICAgYW5pbyA9IGZhY3Rvcih5ZWFyKERBVE9QKSksDQogICAgICAgICB0cmltZXN0cmUgPSBmYWN0b3IocXVhcnRlcnMoREFUT1ApKSwNCiAgICAgICAgIHdlZWtZZWFyID0gd2VlayhEQVRPUCksDQogICAgICAgICBlbmRXZWVrID0gZmFjdG9yKGlmX2Vsc2UoZGF5V2VlayAlaW4lIGMoInPDoWJhZG8iLCAiZG9taW5nbyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJTaSIsIGZhbHNlID0gIk5vIikpLA0KICAgICAgICAgaG9yYVZ1ZWxvID0gaG91cihTVEQpLA0KICAgICAgICAgaG9yYVZ1ZWxvQ2xhcyA9IGlmX2Vsc2UoDQogICAgICAgICAgIGhvcmFWdWVsbyA+PSAwICYgaG9yYVZ1ZWxvIDwgNiwNCiAgICAgICAgICAgdHJ1ZSA9ICJNYWdydWRhZGEiLA0KICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgaG9yYVZ1ZWxvID49IDYgJiBob3JhVnVlbG8gPCAxMiwNCiAgICAgICAgICAgICB0cnVlID0gIk1hw7FhbmEiLA0KICAgICAgICAgICAgIGZhbHNlID0gaWZfZWxzZSgNCiAgICAgICAgICAgICAgIGhvcmFWdWVsbyA+PSAxMiAmIGhvcmFWdWVsbyA8IDE4LA0KICAgICAgICAgICAgICAgdHJ1ZSA9ICJUYXJkZSIsDQogICAgICAgICAgICAgICBmYWxzZSA9ICJOb2NoZSIpKSksDQogICAgICAgICB0aWVtcG9WdWVsbyA9IGFzLm51bWVyaWMoU1RBIC0gU1REKS82MCwNCiAgICAgICAgIHRpZW1wb1Z1ZWxvID0gaWZlbHNlKHRpZW1wb1Z1ZWxvID4gMjQsIE5BLCB0aWVtcG9WdWVsbyksDQogICAgICAgICB0aWVtcG9DbGFzID0gaWZfZWxzZSh0aWVtcG9WdWVsbyA8PSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJDb3J0byIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpZW1wb1Z1ZWxvID4gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJMYXJnbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlID0gIk1vZGVyYWRvIikpKSAlPiUgDQogIGdyb3VwX2J5KERFUFNUTikgJT4lIA0KICBtdXRhdGUodnVlbG9zUGFydGlkYSA9IG4oKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBncm91cF9ieShBUlJTVE4pICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0Rlc3Rpbm8gPSBuKCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoQUMpICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0FDID0gbigpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikgLT4NCiAgbmV3RGF0YVRlc3QNCiMgSnVudGFuZG8gZGF0b3MgZGUgdGllbXBvcyBkZSBlc3BlcmEgKHRhcmdldCkNCmxlZnRfam9pbihuZXdEYXRhVGVzdCwNCiAgICAgICAgICBuZXdEYXRhVHJhaW4gJT4lDQogICAgICAgICAgICBzZWxlY3QoRkxUSUQsIHByb21lZGlvUmV0cmFzbzpkZXN2UmV0cmFzbyksDQogICAgICAgICAgYnkgPSAiRkxUSUQiKSAlPiUNCiAgZGlzdGluY3QoSUQsIC5rZWVwX2FsbCA9IFRSVUUpIC0+DQogIG5ld0RhdGFUZXN0DQpuZXdEYXRhVGVzdA0KYGBgDQoNCiMjIEV4cG9ydGFuZG8gZGF0b3MgMQ0KDQpgYGB7cn0NCiMgTnVldmFzIDENCnNhdmUobmV3RGF0YVRyYWluLCBmaWxlID0gIi4uL215RGF0YS9UcmFpbjEuUmRhdGEiLCBjb21wcmVzcyA9ICJ4eiIpDQpzYXZlKG5ld0RhdGFUZXN0LCBmaWxlID0gIi4uL215RGF0YS9UZXN0MS5SZGF0YSIsIGNvbXByZXNzID0gInh6IikNCmBgYA0KDQojIyBOdWV2YXMgVHJhaW4gMg0KDQpgYGB7cn0NCiMgRmVjaGFzIGVzcGVjaWFsZXMNCmZlY2hhcyA8LSBjKCI1XzEiLCAiOV8xOSIsICIzXzgiLCAiMV8xIiwgIjFfNiIsICIxMF8zMSIsICIxMl8yNCIsICIxMl8yNSIsDQogICAgICAgICAgICAiMTJfMzEiKQ0KDQojIE51ZXZhcyB2YXJpYWJsZXMgdHJhaW4NCmRhdGFUcmFpbiAlPiUgDQogIG11dGF0ZShkYXlXZWVrID0gd2Vla2RheXMoREFUT1ApLA0KICAgICAgICAgbWVzID0gZmFjdG9yKG1vbnRoKERBVE9QKSksDQogICAgICAgICBkaWEgPSBtZGF5KERBVE9QKSwNCiAgICAgICAgIGFuaW8gPSBmYWN0b3IoeWVhcihEQVRPUCkpLA0KICAgICAgICAgdHJpbWVzdHJlID0gZmFjdG9yKHF1YXJ0ZXJzKERBVE9QKSksDQogICAgICAgICB3ZWVrWWVhciA9IHdlZWsoREFUT1ApLA0KICAgICAgICAgZW5kV2VlayA9IGZhY3RvcihpZl9lbHNlKGRheVdlZWsgJWluJSBjKCJzw6FiYWRvIiwgImRvbWluZ28iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpKSwNCiAgICAgICAgIGZpbk1lcyA9IGlmX2Vsc2UoZGlhICVpbiUgYygyOCwgMjksIDMwLCAzMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpLA0KICAgICAgICAgaG9yYVZ1ZWxvID0gaG91cihTVEQpLA0KICAgICAgICAgaG9yYVZ1ZWxvQ2xhcyA9IGlmX2Vsc2UoDQogICAgICAgICAgIGhvcmFWdWVsbyA+PSAwICYgaG9yYVZ1ZWxvIDwgNiwNCiAgICAgICAgICAgdHJ1ZSA9ICJNYWdydWRhZGEiLA0KICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgaG9yYVZ1ZWxvID49IDYgJiBob3JhVnVlbG8gPCAxMiwNCiAgICAgICAgICAgICB0cnVlID0gIk1hw7FhbmEiLA0KICAgICAgICAgICAgIGZhbHNlID0gaWZfZWxzZSgNCiAgICAgICAgICAgICAgIGhvcmFWdWVsbyA+PSAxMiAmIGhvcmFWdWVsbyA8IDE4LA0KICAgICAgICAgICAgICAgdHJ1ZSA9ICJUYXJkZSIsDQogICAgICAgICAgICAgICBmYWxzZSA9ICJOb2NoZSIpKSksDQogICAgICAgICB0aWVtcG9WdWVsbyA9IGFzLm51bWVyaWMoU1RBIC0gU1REKS82MCwNCiAgICAgICAgIHRpZW1wb1Z1ZWxvID0gaWZlbHNlKHRpZW1wb1Z1ZWxvID4gMjQsIE5BLCB0aWVtcG9WdWVsbyksDQogICAgICAgICB0aWVtcG9DbGFzID0gaWZfZWxzZSh0aWVtcG9WdWVsbyA8PSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJDb3J0byIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpZW1wb1Z1ZWxvID4gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJMYXJnbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlID0gIk1vZGVyYWRvIikpKSAlPiUgDQogIGdyb3VwX2J5KERFUFNUTikgJT4lIA0KICBtdXRhdGUodnVlbG9zUGFydGlkYSA9IG4oKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBncm91cF9ieShBUlJTVE4pICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0Rlc3Rpbm8gPSBuKCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoQUMpICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0FDID0gbigpKSAlPiUgDQogIGdyb3VwX2J5KEZMVElEKSAlPiUgDQogIG11dGF0ZShwcm9tZWRpb1JldHJhc28gPSBtZWFuKHRhcmdldCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgIG1lZGlhbmFSZXRyYXNvID0gbWVkaWFuKHRhcmdldCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgIGRlc3ZSZXRyYXNvID0gc2QodGFyZ2V0LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgbWluUmV0cmFzbyA9IG1pbih0YXJnZXQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICBtYXhSZXRyYXNvID0gbWF4KHRhcmdldCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgIFJpY1JldHJhc28gPSBJUVIodGFyZ2V0LCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIHVuaXRlKG1lcywgZGlhLCBjb2wgPSAiZGlhTWVzIiwgcmVtb3ZlID0gRkFMU0UpICU+JSANCiAgbXV0YXRlKGZlY2hhRXNwZWNpYWwgPSBpZl9lbHNlKGRpYU1lcyAlaW4lIGZlY2hhcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpKSAlPiUgDQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikgJT4lIA0KICBzZWxlY3QodGFyZ2V0LCBldmVyeXRoaW5nKCkpICU+JSANCiAgc2VsZWN0KC1kaWFNZXMpIC0+DQogIG5ld0RhdGFUcmFpbg0KICANCm5ld0RhdGFUcmFpbg0KYGBgDQoNCg0KIyMgTnVldmFzIFRlc3QgMg0KDQpgYGB7cn0NCiMgTnVldmFzIHZhcmlhYmxlcyB0ZXN0DQpkYXRhVGVzdCAlPiUgDQogIG11dGF0ZShkYXlXZWVrID0gd2Vla2RheXMoREFUT1ApLA0KICAgICAgICAgbWVzID0gZmFjdG9yKG1vbnRoKERBVE9QKSksDQogICAgICAgICBkaWEgPSBtZGF5KERBVE9QKSwNCiAgICAgICAgIGFuaW8gPSBmYWN0b3IoeWVhcihEQVRPUCkpLA0KICAgICAgICAgdHJpbWVzdHJlID0gZmFjdG9yKHF1YXJ0ZXJzKERBVE9QKSksDQogICAgICAgICB3ZWVrWWVhciA9IHdlZWsoREFUT1ApLA0KICAgICAgICAgZW5kV2VlayA9IGZhY3RvcihpZl9lbHNlKGRheVdlZWsgJWluJSBjKCJzw6FiYWRvIiwgImRvbWluZ28iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpKSwNCiAgICAgICAgIGZpbk1lcyA9IGlmX2Vsc2UoZGlhICVpbiUgYygyOCwgMjksIDMwLCAzMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpLA0KICAgICAgICAgaG9yYVZ1ZWxvID0gaG91cihTVEQpLA0KICAgICAgICAgaG9yYVZ1ZWxvQ2xhcyA9IGlmX2Vsc2UoDQogICAgICAgICAgIGhvcmFWdWVsbyA+PSAwICYgaG9yYVZ1ZWxvIDwgNiwNCiAgICAgICAgICAgdHJ1ZSA9ICJNYWdydWRhZGEiLA0KICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgaG9yYVZ1ZWxvID49IDYgJiBob3JhVnVlbG8gPCAxMiwNCiAgICAgICAgICAgICB0cnVlID0gIk1hw7FhbmEiLA0KICAgICAgICAgICAgIGZhbHNlID0gaWZfZWxzZSgNCiAgICAgICAgICAgICAgIGhvcmFWdWVsbyA+PSAxMiAmIGhvcmFWdWVsbyA8IDE4LA0KICAgICAgICAgICAgICAgdHJ1ZSA9ICJUYXJkZSIsDQogICAgICAgICAgICAgICBmYWxzZSA9ICJOb2NoZSIpKSksDQogICAgICAgICB0aWVtcG9WdWVsbyA9IGFzLm51bWVyaWMoU1RBIC0gU1REKS82MCwNCiAgICAgICAgIHRpZW1wb1Z1ZWxvID0gaWZlbHNlKHRpZW1wb1Z1ZWxvID4gMjQsIE5BLCB0aWVtcG9WdWVsbyksDQogICAgICAgICB0aWVtcG9DbGFzID0gaWZfZWxzZSh0aWVtcG9WdWVsbyA8PSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJDb3J0byIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpZW1wb1Z1ZWxvID4gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJMYXJnbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlID0gIk1vZGVyYWRvIikpKSAlPiUgDQogIHVuaXRlKG1lcywgZGlhLCBjb2wgPSAiZGlhTWVzIiwgcmVtb3ZlID0gRkFMU0UpICU+JSANCiAgbXV0YXRlKGZlY2hhRXNwZWNpYWwgPSBpZl9lbHNlKGRpYU1lcyAlaW4lIGZlY2hhcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpKSAlPiUgDQogIGdyb3VwX2J5KERFUFNUTikgJT4lIA0KICBtdXRhdGUodnVlbG9zUGFydGlkYSA9IG4oKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBncm91cF9ieShBUlJTVE4pICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0Rlc3Rpbm8gPSBuKCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoQUMpICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0FDID0gbigpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIHNlbGVjdCgtZGlhTWVzKSAlPiUgDQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikgLT4NCiAgbmV3RGF0YVRlc3QNCg0KIyBKdW50YW5kbyBkYXRvcyBkZSB0aWVtcG9zIGRlIGVzcGVyYSAodGFyZ2V0KQ0KbGVmdF9qb2luKG5ld0RhdGFUZXN0LA0KICAgICAgICAgIG5ld0RhdGFUcmFpbiAlPiUNCiAgICAgICAgICAgIHNlbGVjdChGTFRJRCwgcHJvbWVkaW9SZXRyYXNvOlJpY1JldHJhc28pLA0KICAgICAgICAgIGJ5ID0gIkZMVElEIikgJT4lDQogIGRpc3RpbmN0KElELCAua2VlcF9hbGwgPSBUUlVFKSAtPg0KICBuZXdEYXRhVGVzdA0KDQpuZXdEYXRhVGVzdA0KYGBgDQoNCiMjIEV4cG9ydGFuZG8gZGF0b3MgMg0KDQpgYGB7cn0NCiMgTnVldmFzIDINCnNhdmUobmV3RGF0YVRyYWluLCBmaWxlID0gIi4uL215RGF0YS9UcmFpbjIuUmRhdGEiLCBjb21wcmVzcyA9ICJ4eiIpDQpzYXZlKG5ld0RhdGFUZXN0LCBmaWxlID0gIi4uL215RGF0YS9UZXN0Mi5SZGF0YSIsIGNvbXByZXNzID0gInh6IikNCmBgYA0KDQojIE1vZGVsb3MNCg0KIyMgTW9kZWxvIDENCg0KYGBge3J9DQojIExvYWQgZGF0YQ0KbG9hZCgiLi4vbXlEYXRhL1RyYWluMS5SZGF0YSIpDQpsb2FkKCIuLi9teURhdGEvVGVzdDEuUmRhdGEiKQ0KZGF0YVNhbXBsZSA8LSBkYXRhLnRhYmxlOjpmcmVhZCgiLi4vZGF0YS9zYW1wbGUuY3N2IikNCg0KbmV3RGF0YVRyYWluIDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRyYWluKQ0KbmV3RGF0YVRlc3QgPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVGVzdCkNCg0KIyBTZWxlY3Rpb24gb2YgdmFyaWFibGVzIGZvciBhbmFseXNpcyANCmxpYnJhcnkodGlkeXZlcnNlKQ0KZGF0YVRyYWluIDwtIG5ld0RhdGFUcmFpbiAlPiUNCiAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKQ0KDQojIGNhcmV0IGZvciBwYXJ0aXRpb24gZGF0YQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZHggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gZGF0YVRyYWluJHRhcmdldCwgdGltZXMgPSAxLCBwID0gMC43MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UpDQpkZlRyYWluIDwtIGRhdGFUcmFpbltpbmR4LCBdDQpkZlRlc3QgPC0gZGF0YVRyYWluWy1pbmR4LCBdDQoNCiMgQ2F0ZWdvcmljYWwgZmVhdHVyZXMNCmNhdEZlYXR1cmVzIDwtIG5hbWVzKA0KICBkYXRhVHJhaW4gJT4lIHNlbGVjdF9pZihpcy5mYWN0b3IpDQopDQoNCiMgRGF0YSBmb3IgbGlnaHRnYm0NCmxpYnJhcnkobGlnaHRnYm0pDQpkYXRhVHJhaW5fbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUcmFpblssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVHJhaW5bLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCmRhdGFUZXN0X2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVGVzdFssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUZXN0WywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4xLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMSwNCiAgYmFnZ2luZ19mcmFjdGlvbiA9IDEsDQogIG1heF9kZXB0aCA9IC0xDQopDQoNCiMgVHJhaW4gbW9kZWwNCm1vZGVsbyA8LSBsZ2IudHJhaW4ocGFyYW1zID0gbXlQYXJhbXMsDQogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhVHJhaW5fbGdibSwNCiAgICAgICAgICAgICAgICAgICAgbnJvdW5kcyA9IDEwMDAwLA0KICAgICAgICAgICAgICAgICAgICB2YWxpZHMgPSBsaXN0KHRlc3QgPSBkYXRhVGVzdF9sZ2JtKSwNCiAgICAgICAgICAgICAgICAgICAgZWFybHlfc3RvcHBpbmdfcm91bmRzID0gNTAwKQ0KDQojYmVzdCBpdGVyOiAyMzgNCiNiZXN0IHNjb3JlOiAxMDcuOTkwMw0KDQojIFByZWRpY3Rpb25zDQpwcmVkaWNjaW9uZXMgPC0gcHJlZGljdChtb2RlbG8sIGRhdGEubWF0cml4KG5ld0RhdGFUZXN0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkpLCANCiAgICAgICAgICAgICAgICAgICAgICAgIG51bV9pdGVyYXRpb24gPSBtb2RlbG8kYmVzdF9pdGVyKQ0KDQpwcmVkaWNjaW9uZXNbcHJlZGljY2lvbmVzIDwgMF0gPC0gMA0KeDExKCk7aGlzdChwcmVkaWNjaW9uZXMpDQoNCiMgU3VibWlzc2lvbg0KZGF0YVNhbXBsZSAlPiUgDQogIHNlbGVjdChJRCkgJT4lIA0KICBtdXRhdGUodGFyZ2V0ID0gcHJlZGljY2lvbmVzKSAtPg0KICBsZ2JtUjENCg0KIyBFeHBvcnQgc3VibWlzc2lvbiBmb3IgemluZGkNCndyaXRlLmNzdihsZ2JtUjEsIGZpbGUgPSAiLi4vc3VibWlzc2lvbi9sZ2JtUjFWdWVsb3MuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KIyMgTW9kZWxvIDINCg0KYGBge3J9DQojIExvYWQgZGF0YQ0KbG9hZCgiLi4vbXlEYXRhL1RyYWluMS5SZGF0YSIpDQpsb2FkKCIuLi9teURhdGEvVGVzdDEuUmRhdGEiKQ0KZGF0YVNhbXBsZSA8LSBkYXRhLnRhYmxlOjpmcmVhZCgiLi4vZGF0YS9zYW1wbGUuY3N2IikNCg0KbmV3RGF0YVRyYWluIDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRyYWluKQ0KbmV3RGF0YVRlc3QgPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVGVzdCkNCg0KIyBTZWxlY3Rpb24gb2YgdmFyaWFibGVzIGZvciBhbmFseXNpcyANCmxpYnJhcnkodGlkeXZlcnNlKQ0KZGF0YVRyYWluIDwtIG5ld0RhdGFUcmFpbiAlPiUNCiAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKQ0KDQojIGNhcmV0IGZvciBwYXJ0aXRpb24gZGF0YQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZHggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gZGF0YVRyYWluJHRhcmdldCwgdGltZXMgPSAxLCBwID0gMC43MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UpDQpkZlRyYWluIDwtIGRhdGFUcmFpbltpbmR4LCBdDQpkZlRlc3QgPC0gZGF0YVRyYWluWy1pbmR4LCBdDQoNCiMgQ2F0ZWdvcmljYWwgZmVhdHVyZXMNCmNhdEZlYXR1cmVzIDwtIG5hbWVzKA0KICBkYXRhVHJhaW4gJT4lIHNlbGVjdF9pZihpcy5mYWN0b3IpDQopDQoNCiMgRGF0YSBmb3IgbGlnaHRnYm0NCmxpYnJhcnkobGlnaHRnYm0pDQpkYXRhVHJhaW5fbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUcmFpblssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVHJhaW5bLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCmRhdGFUZXN0X2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVGVzdFssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUZXN0WywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4xLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMC41LA0KICBiYWdnaW5nX2ZyYWN0aW9uID0gMSwNCiAgbWluX2RhdGFfaW5fbGVhZiA9IDEwMCwNCiAgbnVtX2xlYXZlcyA9IDI1NSwNCiAgbWF4X2RlcHRoID0gLTENCikNCg0KIyBUcmFpbiBtb2RlbA0KbW9kZWxvIDwtIGxnYi50cmFpbihwYXJhbXMgPSBteVBhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFUcmFpbl9sZ2JtLA0KICAgICAgICAgICAgICAgICAgICBucm91bmRzID0gMTAwMDAsDQogICAgICAgICAgICAgICAgICAgIHZhbGlkcyA9IGxpc3QodGVzdCA9IGRhdGFUZXN0X2xnYm0pLA0KICAgICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MDApDQoNCiNiZXN0IGl0ZXI6IDY5DQojYmVzdCBzY29yZTogMTA5LjA2NTENCg0KIyBQcmVkaWN0aW9ucw0KcHJlZGljY2lvbmVzIDwtIHByZWRpY3QobW9kZWxvLCBkYXRhLm1hdHJpeChuZXdEYXRhVGVzdCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSkpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBudW1faXRlcmF0aW9uID0gbW9kZWxvJGJlc3RfaXRlcikNCg0KcHJlZGljY2lvbmVzW3ByZWRpY2Npb25lcyA8IDBdIDwtIDANCngxMSgpO2hpc3QocHJlZGljY2lvbmVzKQ0KDQojIFN1Ym1pc3Npb24NCmRhdGFTYW1wbGUgJT4lIA0KICBzZWxlY3QoSUQpICU+JSANCiAgbXV0YXRlKHRhcmdldCA9IHByZWRpY2Npb25lcykgLT4NCiAgbGdibVIyDQoNCiMgRXhwb3J0IHN1Ym1pc3Npb24gZm9yIHppbmRpDQp3cml0ZS5jc3YobGdibVIyLCBmaWxlID0gIi4uL3N1Ym1pc3Npb24vbGdibVIyVnVlbG9zLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCg0KIyMgTW9kZWxvIDMNCg0KYGBge3J9DQojIExvYWQgZGF0YQ0KbG9hZCgiLi4vbXlEYXRhL1RyYWluMS5SZGF0YSIpDQpsb2FkKCIuLi9teURhdGEvVGVzdDEuUmRhdGEiKQ0KZGF0YVNhbXBsZSA8LSBkYXRhLnRhYmxlOjpmcmVhZCgiLi4vZGF0YS9zYW1wbGUuY3N2IikNCg0KbmV3RGF0YVRyYWluIDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRyYWluKQ0KbmV3RGF0YVRlc3QgPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVGVzdCkNCg0KIyBTZWxlY3Rpb24gb2YgdmFyaWFibGVzIGZvciBhbmFseXNpcyANCmxpYnJhcnkodGlkeXZlcnNlKQ0KZGF0YVRyYWluIDwtIG5ld0RhdGFUcmFpbiAlPiUNCiAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKQ0KDQojIGNhcmV0IGZvciBwYXJ0aXRpb24gZGF0YQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZHggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gZGF0YVRyYWluJHRhcmdldCwgdGltZXMgPSAxLCBwID0gMC44MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UpDQpkZlRyYWluIDwtIGRhdGFUcmFpbltpbmR4LCBdDQpkZlRlc3QgPC0gZGF0YVRyYWluWy1pbmR4LCBdDQoNCiMgQ2F0ZWdvcmljYWwgZmVhdHVyZXMNCmNhdEZlYXR1cmVzIDwtIG5hbWVzKA0KICBkYXRhVHJhaW4gJT4lIHNlbGVjdF9pZihpcy5mYWN0b3IpDQopDQoNCiMgRGF0YSBmb3IgbGlnaHRnYm0NCmxpYnJhcnkobGlnaHRnYm0pDQpkYXRhVHJhaW5fbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUcmFpblssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVHJhaW5bLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCmRhdGFUZXN0X2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVGVzdFssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUZXN0WywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4wMSwNCiAgZmVhdHVyZV9mcmFjdGlvbiA9IDEsDQogIGJhZ2dpbmdfZnJhY3Rpb24gPSAxLA0KICBtYXhfZGVwdGggPSAtMQ0KKQ0KDQojIFRyYWluIG1vZGVsDQptb2RlbG8gPC0gbGdiLnRyYWluKHBhcmFtcyA9IG15UGFyYW1zLA0KICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YVRyYWluX2xnYm0sDQogICAgICAgICAgICAgICAgICAgIG5yb3VuZHMgPSAxMDAwMCwNCiAgICAgICAgICAgICAgICAgICAgdmFsaWRzID0gbGlzdCh0ZXN0ID0gZGF0YVRlc3RfbGdibSksDQogICAgICAgICAgICAgICAgICAgIGVhcmx5X3N0b3BwaW5nX3JvdW5kcyA9IDUwMCkNCg0KI2Jlc3QgaXRlcjogMTQyNg0KI2Jlc3Qgc2NvcmU6IDEwNi44Mzg0DQoNCiMgUHJlZGljdGlvbnMNCnByZWRpY2Npb25lcyA8LSBwcmVkaWN0KG1vZGVsbywgZGF0YS5tYXRyaXgobmV3RGF0YVRlc3QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2l0ZXJhdGlvbiA9IG1vZGVsbyRiZXN0X2l0ZXIpDQoNCnByZWRpY2Npb25lc1twcmVkaWNjaW9uZXMgPCAwXSA8LSAwDQp4MTEoKTtoaXN0KHByZWRpY2Npb25lcykNCg0KIyBTdWJtaXNzaW9uDQpkYXRhU2FtcGxlICU+JSANCiAgc2VsZWN0KElEKSAlPiUgDQogIG11dGF0ZSh0YXJnZXQgPSBwcmVkaWNjaW9uZXMpIC0+DQogIGxnYm1SMw0KDQojIEV4cG9ydCBzdWJtaXNzaW9uIGZvciB6aW5kaQ0Kd3JpdGUuY3N2KGxnYm1SMywgZmlsZSA9ICIuLi9zdWJtaXNzaW9uL2xnYm1SM1Z1ZWxvcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBNb2RlbG8gNA0KDQpgYGB7cn0NCiMgTG9hZCBkYXRhDQpsb2FkKCIuLi9teURhdGEvVHJhaW4xLlJkYXRhIikNCmxvYWQoIi4uL215RGF0YS9UZXN0MS5SZGF0YSIpDQpkYXRhU2FtcGxlIDwtIGRhdGEudGFibGU6OmZyZWFkKCIuLi9kYXRhL3NhbXBsZS5jc3YiKQ0KDQpuZXdEYXRhVHJhaW4gPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVHJhaW4pDQpuZXdEYXRhVGVzdCA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUZXN0KQ0KDQojIFNlbGVjdGlvbiBvZiB2YXJpYWJsZXMgZm9yIGFuYWx5c2lzIA0KbGlicmFyeSh0aWR5dmVyc2UpDQpkYXRhVHJhaW4gPC0gbmV3RGF0YVRyYWluICU+JQ0KICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSkpDQoNCiMgY2FyZXQgZm9yIHBhcnRpdGlvbiBkYXRhDQpsaWJyYXJ5KGNhcmV0KQ0Kc2V0LnNlZWQoMTIzKQ0KaW5keCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBkYXRhVHJhaW4kdGFyZ2V0LCB0aW1lcyA9IDEsIHAgPSAwLjgwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSkNCmRmVHJhaW4gPC0gZGF0YVRyYWluW2luZHgsIF0NCmRmVGVzdCA8LSBkYXRhVHJhaW5bLWluZHgsIF0NCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBEYXRhIGZvciBsaWdodGdibQ0KbGlicmFyeShsaWdodGdibSkNCmRhdGFUcmFpbl9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkZlRyYWluWywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUcmFpblssIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KZGF0YVRlc3RfbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUZXN0WywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkZlRlc3RbLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KDQojIFBhcmFtZXRlcnMgZm9yIGxpZ2h0Z2JtDQpteVBhcmFtcyA8LSBsaXN0KA0KICBib29zdGluZyA9ICJnYmR0IiwNCiAgb2JqZWN0aXZlID0gInJlZ3Jlc3Npb24iLA0KICBtZXRyaWMgPSAicm1zZSIsDQogIGxlYXJuaW5nX3JhdGUgPSAwLjAxLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMC41LA0KICBiYWdnaW5nX2ZyYWN0aW9uID0gMSwNCiAgbWluX2RhdGFfaW5fbGVhZiA9IDEwMCwNCiAgbnVtX2xlYXZlcyA9IDI1NSwNCiAgbWF4X2RlcHRoID0gLTENCikNCg0KIyBUcmFpbiBtb2RlbA0KbW9kZWxvIDwtIGxnYi50cmFpbihwYXJhbXMgPSBteVBhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFUcmFpbl9sZ2JtLA0KICAgICAgICAgICAgICAgICAgICBucm91bmRzID0gMTAwMDAsDQogICAgICAgICAgICAgICAgICAgIHZhbGlkcyA9IGxpc3QodGVzdCA9IGRhdGFUZXN0X2xnYm0pLA0KICAgICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MDApDQoNCiNiZXN0IGl0ZXI6IDg3OA0KI2Jlc3Qgc2NvcmU6IDEwNi45MjQ3DQoNCiMgUHJlZGljdGlvbnMNCnByZWRpY2Npb25lcyA8LSBwcmVkaWN0KG1vZGVsbywgZGF0YS5tYXRyaXgobmV3RGF0YVRlc3QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2l0ZXJhdGlvbiA9IG1vZGVsbyRiZXN0X2l0ZXIpDQoNCnByZWRpY2Npb25lc1twcmVkaWNjaW9uZXMgPCAwXSA8LSAwDQp4MTEoKTtoaXN0KHByZWRpY2Npb25lcykNCg0KIyBTdWJtaXNzaW9uDQpkYXRhU2FtcGxlICU+JSANCiAgc2VsZWN0KElEKSAlPiUgDQogIG11dGF0ZSh0YXJnZXQgPSBwcmVkaWNjaW9uZXMpIC0+DQogIGxnYm1SNA0KDQojIEV4cG9ydCBzdWJtaXNzaW9uIGZvciB6aW5kaQ0Kd3JpdGUuY3N2KGxnYm1SNCwgZmlsZSA9ICIuLi9zdWJtaXNzaW9uL2xnYm1SNFZ1ZWxvcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBNb2RlbG8gNQ0KDQpgYGB7cn0NCiMgTG9hZCBkYXRhDQpsb2FkKCIuLi9teURhdGEvVHJhaW4xLlJkYXRhIikNCmxvYWQoIi4uL215RGF0YS9UZXN0MS5SZGF0YSIpDQpkYXRhU2FtcGxlIDwtIGRhdGEudGFibGU6OmZyZWFkKCIuLi9kYXRhL3NhbXBsZS5jc3YiKQ0KDQpuZXdEYXRhVHJhaW4gPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVHJhaW4pDQpuZXdEYXRhVGVzdCA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUZXN0KQ0KDQojIFNlbGVjdGlvbiBvZiB2YXJpYWJsZXMgZm9yIGFuYWx5c2lzIA0KbGlicmFyeSh0aWR5dmVyc2UpDQpkYXRhVHJhaW4gPC0gbmV3RGF0YVRyYWluICU+JQ0KICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSkpDQoNCiMgY2FyZXQgZm9yIHBhcnRpdGlvbiBkYXRhDQpsaWJyYXJ5KGNhcmV0KQ0Kc2V0LnNlZWQoMTIzKQ0KaW5keCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBkYXRhVHJhaW4kdGFyZ2V0LCB0aW1lcyA9IDEsIHAgPSAwLjgwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSkNCmRmVHJhaW4gPC0gZGF0YVRyYWluW2luZHgsIF0NCmRmVGVzdCA8LSBkYXRhVHJhaW5bLWluZHgsIF0NCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBEYXRhIGZvciBsaWdodGdibQ0KbGlicmFyeShsaWdodGdibSkNCmRhdGFUcmFpbl9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkZlRyYWluWywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUcmFpblssIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KZGF0YVRlc3RfbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUZXN0WywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkZlRlc3RbLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KDQojIFBhcmFtZXRlcnMgZm9yIGxpZ2h0Z2JtDQpteVBhcmFtcyA8LSBsaXN0KA0KICBib29zdGluZyA9ICJnYmR0IiwNCiAgb2JqZWN0aXZlID0gInJlZ3Jlc3Npb24iLA0KICBtZXRyaWMgPSAicm1zZSIsDQogIGxlYXJuaW5nX3JhdGUgPSAwLjAxLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMSwNCiAgYmFnZ2luZ19mcmFjdGlvbiA9IDEsDQogIG1pbl9kYXRhX2luX2xlYWYgPSAzMDAsDQogIG51bV9sZWF2ZXMgPSA1MDAsDQogIG1heF9iaW4gPSA1MDAsDQogIG1heF9kZXB0aCA9IC0xDQopDQoNCiMgVHJhaW4gbW9kZWwNCm1vZGVsbyA8LSBsZ2IudHJhaW4ocGFyYW1zID0gbXlQYXJhbXMsDQogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhVHJhaW5fbGdibSwNCiAgICAgICAgICAgICAgICAgICAgbnJvdW5kcyA9IDEwMDAwLA0KICAgICAgICAgICAgICAgICAgICB2YWxpZHMgPSBsaXN0KHRlc3QgPSBkYXRhVGVzdF9sZ2JtKSwNCiAgICAgICAgICAgICAgICAgICAgZWFybHlfc3RvcHBpbmdfcm91bmRzID0gNTAwKQ0KDQojYmVzdCBpdGVyOiA2ODkNCiNiZXN0IHNjb3JlOiAxMDguMTE3Ng0KDQojIFByZWRpY3Rpb25zDQpwcmVkaWNjaW9uZXMgPC0gcHJlZGljdChtb2RlbG8sIGRhdGEubWF0cml4KG5ld0RhdGFUZXN0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkpLCANCiAgICAgICAgICAgICAgICAgICAgICAgIG51bV9pdGVyYXRpb24gPSBtb2RlbG8kYmVzdF9pdGVyKQ0KDQpwcmVkaWNjaW9uZXNbcHJlZGljY2lvbmVzIDwgMF0gPC0gMA0KeDExKCk7aGlzdChwcmVkaWNjaW9uZXMpDQoNCiMgU3VibWlzc2lvbg0KZGF0YVNhbXBsZSAlPiUgDQogIHNlbGVjdChJRCkgJT4lIA0KICBtdXRhdGUodGFyZ2V0ID0gcHJlZGljY2lvbmVzKSAtPg0KICBsZ2JtUjUNCg0KIyBFeHBvcnQgc3VibWlzc2lvbiBmb3IgemluZGkNCndyaXRlLmNzdihsZ2JtUjUsIGZpbGUgPSAiLi4vc3VibWlzc2lvbi9sZ2JtUjVWdWVsb3MuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KIyMgTW9kZWxvIDYNCg0KYGBge3J9DQojIExvYWQgZGF0YQ0KbG9hZCgiLi4vbXlEYXRhL1RyYWluMi5SZGF0YSIpDQpsb2FkKCIuLi9teURhdGEvVGVzdDIuUmRhdGEiKQ0KZGF0YVNhbXBsZSA8LSBkYXRhLnRhYmxlOjpmcmVhZCgiLi4vZGF0YS9zYW1wbGUuY3N2IikNCg0KbmV3RGF0YVRyYWluIDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRyYWluKQ0KbmV3RGF0YVRlc3QgPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVGVzdCkNCg0KIyBTZWxlY3Rpb24gb2YgdmFyaWFibGVzIGZvciBhbmFseXNpcyANCmxpYnJhcnkodGlkeXZlcnNlKQ0KZGF0YVRyYWluIDwtIG5ld0RhdGFUcmFpbiAlPiUNCiAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEsIG1pblJldHJhc28sIG1heFJldHJhc28pKQ0KDQojIGNhcmV0IGZvciBwYXJ0aXRpb24gZGF0YQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZHggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gZGF0YVRyYWluJHRhcmdldCwgdGltZXMgPSAxLCBwID0gMC44MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UpDQpkZlRyYWluIDwtIGRhdGFUcmFpbltpbmR4LCBdDQpkZlRlc3QgPC0gZGF0YVRyYWluWy1pbmR4LCBdDQoNCiMgQ2F0ZWdvcmljYWwgZmVhdHVyZXMNCmNhdEZlYXR1cmVzIDwtIG5hbWVzKA0KICBkYXRhVHJhaW4gJT4lIHNlbGVjdF9pZihpcy5mYWN0b3IpDQopDQoNCiMgRGF0YSBmb3IgbGlnaHRnYm0NCmxpYnJhcnkobGlnaHRnYm0pDQpkYXRhVHJhaW5fbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUcmFpblssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVHJhaW5bLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCmRhdGFUZXN0X2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVGVzdFssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUZXN0WywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4wMSwNCiAgZmVhdHVyZV9mcmFjdGlvbiA9IDEsDQogIGJhZ2dpbmdfZnJhY3Rpb24gPSAxLA0KICBtYXhfZGVwdGggPSAtMQ0KKQ0KDQojIFRyYWluIG1vZGVsDQptb2RlbG8gPC0gbGdiLnRyYWluKHBhcmFtcyA9IG15UGFyYW1zLA0KICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YVRyYWluX2xnYm0sDQogICAgICAgICAgICAgICAgICAgIG5yb3VuZHMgPSAxMDAwMCwNCiAgICAgICAgICAgICAgICAgICAgdmFsaWRzID0gbGlzdCh0ZXN0ID0gZGF0YVRlc3RfbGdibSksDQogICAgICAgICAgICAgICAgICAgIGVhcmx5X3N0b3BwaW5nX3JvdW5kcyA9IDUwMCkNCg0KI2Jlc3QgaXRlcjogMTYyMA0KI2Jlc3Qgc2NvcmU6IDEwNS42NDg2DQoNCiMgUHJlZGljdGlvbnMNCnByZWRpY2Npb25lcyA8LSBwcmVkaWN0KG1vZGVsbywgZGF0YS5tYXRyaXgobmV3RGF0YVRlc3QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pblJldHJhc28sIG1heFJldHJhc28pKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2l0ZXJhdGlvbiA9IG1vZGVsbyRiZXN0X2l0ZXIpDQoNCnByZWRpY2Npb25lc1twcmVkaWNjaW9uZXMgPCAwXSA8LSAwDQp4MTEoKTtoaXN0KHByZWRpY2Npb25lcykNCg0KIyBTdWJtaXNzaW9uDQpkYXRhU2FtcGxlICU+JSANCiAgc2VsZWN0KElEKSAlPiUgDQogIG11dGF0ZSh0YXJnZXQgPSBwcmVkaWNjaW9uZXMpIC0+DQogIGxnYm1SNg0KDQojIEV4cG9ydCBzdWJtaXNzaW9uIGZvciB6aW5kaQ0Kd3JpdGUuY3N2KGxnYm1SNiwgZmlsZSA9ICIuLi9zdWJtaXNzaW9uL2xnYm1SNlZ1ZWxvcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBNb2RlbG8gNw0KDQpgYGB7cn0NCiMgTG9hZCBkYXRhDQpsb2FkKCIuLi9teURhdGEvVHJhaW4yLlJkYXRhIikNCmxvYWQoIi4uL215RGF0YS9UZXN0Mi5SZGF0YSIpDQpkYXRhU2FtcGxlIDwtIGRhdGEudGFibGU6OmZyZWFkKCIuLi9kYXRhL3NhbXBsZS5jc3YiKQ0KDQpuZXdEYXRhVHJhaW4gPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVHJhaW4pDQpuZXdEYXRhVGVzdCA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUZXN0KQ0KDQojIFNlbGVjdGlvbiBvZiB2YXJpYWJsZXMgZm9yIGFuYWx5c2lzIA0KbGlicmFyeSh0aWR5dmVyc2UpDQpkYXRhVHJhaW4gPC0gbmV3RGF0YVRyYWluICU+JQ0KICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSwgZGlhLCBtaW5SZXRyYXNvLCBtYXhSZXRyYXNvLCBSaWNSZXRyYXNvLCBmZWNoYUVzcGVjaWFsKSkNCg0KIyBjYXJldCBmb3IgcGFydGl0aW9uIGRhdGENCmxpYnJhcnkoY2FyZXQpDQpzZXQuc2VlZCgxMjMpDQppbmR4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRhdGFUcmFpbiR0YXJnZXQsIHRpbWVzID0gMSwgcCA9IDAuODAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFKQ0KZGZUcmFpbiA8LSBkYXRhVHJhaW5baW5keCwgXQ0KZGZUZXN0IDwtIGRhdGFUcmFpblstaW5keCwgXQ0KDQojIENhdGVnb3JpY2FsIGZlYXR1cmVzDQpjYXRGZWF0dXJlcyA8LSBuYW1lcygNCiAgZGF0YVRyYWluICU+JSBzZWxlY3RfaWYoaXMuZmFjdG9yKQ0KKQ0KDQojIERhdGEgZm9yIGxpZ2h0Z2JtDQpsaWJyYXJ5KGxpZ2h0Z2JtKQ0KZGF0YVRyYWluX2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVHJhaW5bLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkZlRyYWluWywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQpkYXRhVGVzdF9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkZlRlc3RbLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVGVzdFssIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQoNCiMgUGFyYW1ldGVycyBmb3IgbGlnaHRnYm0NCm15UGFyYW1zIDwtIGxpc3QoDQogIGJvb3N0aW5nID0gImdiZHQiLA0KICBvYmplY3RpdmUgPSAicmVncmVzc2lvbiIsDQogIG1ldHJpYyA9ICJybXNlIiwNCiAgbGVhcm5pbmdfcmF0ZSA9IDAuMDEsDQogIGZlYXR1cmVfZnJhY3Rpb24gPSAxLA0KICBiYWdnaW5nX2ZyYWN0aW9uID0gMSwNCiAgbWF4X2RlcHRoID0gLTENCikNCg0KIyBUcmFpbiBtb2RlbA0KbW9kZWxvIDwtIGxnYi50cmFpbihwYXJhbXMgPSBteVBhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFUcmFpbl9sZ2JtLA0KICAgICAgICAgICAgICAgICAgICBucm91bmRzID0gMTAwMDAsDQogICAgICAgICAgICAgICAgICAgIHZhbGlkcyA9IGxpc3QodGVzdCA9IGRhdGFUZXN0X2xnYm0pLA0KICAgICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MDApDQoNCiNiZXN0IGl0ZXI6IDE1MTENCiNiZXN0IHNjb3JlOiAxMDYuNjU0OQ0KDQojIFByZWRpY3Rpb25zDQpwcmVkaWNjaW9uZXMgPC0gcHJlZGljdChtb2RlbG8sIGRhdGEubWF0cml4KG5ld0RhdGFUZXN0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWEsIG1pblJldHJhc28sIG1heFJldHJhc28sIFJpY1JldHJhc28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlY2hhRXNwZWNpYWwpKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2l0ZXJhdGlvbiA9IG1vZGVsbyRiZXN0X2l0ZXIpDQoNCnByZWRpY2Npb25lc1twcmVkaWNjaW9uZXMgPCAwXSA8LSAwDQp4MTEoKTtoaXN0KHByZWRpY2Npb25lcykNCg0KIyBTdWJtaXNzaW9uDQpkYXRhU2FtcGxlICU+JSANCiAgc2VsZWN0KElEKSAlPiUgDQogIG11dGF0ZSh0YXJnZXQgPSBwcmVkaWNjaW9uZXMpIC0+DQogIGxnYm1SNw0KDQojIEV4cG9ydCBzdWJtaXNzaW9uIGZvciB6aW5kaQ0Kd3JpdGUuY3N2KGxnYm1SNywgZmlsZSA9ICIuLi9zdWJtaXNzaW9uL2xnYm1SN1Z1ZWxvcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBNb2RlbG8gOA0KDQpgYGB7cn0NCiMgTG9hZCBkYXRhDQpsb2FkKCIuLi9teURhdGEvVHJhaW4yLlJkYXRhIikNCmxvYWQoIi4uL215RGF0YS9UZXN0Mi5SZGF0YSIpDQpkYXRhU2FtcGxlIDwtIGRhdGEudGFibGU6OmZyZWFkKCIuLi9kYXRhL3NhbXBsZS5jc3YiKQ0KDQpuZXdEYXRhVHJhaW4gPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVHJhaW4pDQpuZXdEYXRhVGVzdCA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUZXN0KQ0KDQojIFNlbGVjdGlvbiBvZiB2YXJpYWJsZXMgZm9yIGFuYWx5c2lzIA0KbGlicmFyeSh0aWR5dmVyc2UpDQpkYXRhVHJhaW4gPC0gbmV3RGF0YVRyYWluICU+JQ0KICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSwgZmVjaGFFc3BlY2lhbCkpDQoNCiMgY2FyZXQgZm9yIHBhcnRpdGlvbiBkYXRhDQpsaWJyYXJ5KGNhcmV0KQ0Kc2V0LnNlZWQoMTIzKQ0KaW5keCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBkYXRhVHJhaW4kdGFyZ2V0LCB0aW1lcyA9IDEsIHAgPSAwLjgwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSkNCmRmVHJhaW4gPC0gZGF0YVRyYWluW2luZHgsIF0NCmRmVGVzdCA8LSBkYXRhVHJhaW5bLWluZHgsIF0NCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBEYXRhIGZvciBsaWdodGdibQ0KbGlicmFyeShsaWdodGdibSkNCmRhdGFUcmFpbl9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkZlRyYWluWywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUcmFpblssIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KZGF0YVRlc3RfbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUZXN0WywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkZlRlc3RbLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KDQojIFBhcmFtZXRlcnMgZm9yIGxpZ2h0Z2JtDQpteVBhcmFtcyA8LSBsaXN0KA0KICBib29zdGluZyA9ICJnYmR0IiwNCiAgb2JqZWN0aXZlID0gInJlZ3Jlc3Npb24iLA0KICBtZXRyaWMgPSAicm1zZSIsDQogIGxlYXJuaW5nX3JhdGUgPSAwLjAxLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMSwNCiAgYmFnZ2luZ19mcmFjdGlvbiA9IDEsDQogIG1heF9kZXB0aCA9IC0xDQopDQoNCiMgVHJhaW4gbW9kZWwNCm1vZGVsbyA8LSBsZ2IudHJhaW4ocGFyYW1zID0gbXlQYXJhbXMsDQogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhVHJhaW5fbGdibSwNCiAgICAgICAgICAgICAgICAgICAgbnJvdW5kcyA9IDEwMDAwLA0KICAgICAgICAgICAgICAgICAgICB2YWxpZHMgPSBsaXN0KHRlc3QgPSBkYXRhVGVzdF9sZ2JtKSwNCiAgICAgICAgICAgICAgICAgICAgZWFybHlfc3RvcHBpbmdfcm91bmRzID0gNTAwKQ0KDQojYmVzdCBpdGVyOiAxNTA0DQojYmVzdCBzY29yZTogMTA1Ljc4NjYNCg0KIyBQcmVkaWN0aW9ucw0KcHJlZGljY2lvbmVzIDwtIHByZWRpY3QobW9kZWxvLCBkYXRhLm1hdHJpeChuZXdEYXRhVGVzdCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSwgZmVjaGFFc3BlY2lhbCkpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBudW1faXRlcmF0aW9uID0gbW9kZWxvJGJlc3RfaXRlcikNCg0KcHJlZGljY2lvbmVzW3ByZWRpY2Npb25lcyA8IDBdIDwtIDANCngxMSgpO2hpc3QocHJlZGljY2lvbmVzKQ0KDQojIFN1Ym1pc3Npb24NCmRhdGFTYW1wbGUgJT4lIA0KICBzZWxlY3QoSUQpICU+JSANCiAgbXV0YXRlKHRhcmdldCA9IHByZWRpY2Npb25lcykgLT4NCiAgbGdibVI4DQoNCiMgRXhwb3J0IHN1Ym1pc3Npb24gZm9yIHppbmRpDQp3cml0ZS5jc3YobGdibVI4LCBmaWxlID0gIi4uL3N1Ym1pc3Npb24vbGdibVI4VnVlbG9zLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCiMjIE1vZGVsbyA5DQoNCmBgYHtyfQ0KIyBMb2FkIGRhdGENCmxvYWQoIi4uL215RGF0YS9UcmFpbjIuUmRhdGEiKQ0KbG9hZCgiLi4vbXlEYXRhL1Rlc3QyLlJkYXRhIikNCmRhdGFTYW1wbGUgPC0gZGF0YS50YWJsZTo6ZnJlYWQoIi4uL2RhdGEvc2FtcGxlLmNzdiIpDQoNCm5ld0RhdGFUcmFpbiA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUcmFpbikNCm5ld0RhdGFUZXN0IDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRlc3QpDQoNCiMgU2VsZWN0aW9uIG9mIHZhcmlhYmxlcyBmb3IgYW5hbHlzaXMgDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmRhdGFUcmFpbiA8LSBuZXdEYXRhVHJhaW4gJT4lDQogIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBLCBmZWNoYUVzcGVjaWFsKSkNCg0KIyBjYXJldCBmb3IgcGFydGl0aW9uIGRhdGENCmxpYnJhcnkoY2FyZXQpDQpzZXQuc2VlZCgxMjMpDQppbmR4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRhdGFUcmFpbiR0YXJnZXQsIHRpbWVzID0gMSwgcCA9IDAuOTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFKQ0KZGZUcmFpbiA8LSBkYXRhVHJhaW5baW5keCwgXQ0KZGZUZXN0IDwtIGRhdGFUcmFpblstaW5keCwgXQ0KDQojIENhdGVnb3JpY2FsIGZlYXR1cmVzDQpjYXRGZWF0dXJlcyA8LSBuYW1lcygNCiAgZGF0YVRyYWluICU+JSBzZWxlY3RfaWYoaXMuZmFjdG9yKQ0KKQ0KDQojIERhdGEgZm9yIGxpZ2h0Z2JtDQpsaWJyYXJ5KGxpZ2h0Z2JtKQ0KZGF0YVRyYWluX2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVHJhaW5bLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkZlRyYWluWywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQpkYXRhVGVzdF9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkZlRlc3RbLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVGVzdFssIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQoNCiMgUGFyYW1ldGVycyBmb3IgbGlnaHRnYm0NCm15UGFyYW1zIDwtIGxpc3QoDQogIGJvb3N0aW5nID0gImdiZHQiLA0KICBvYmplY3RpdmUgPSAicmVncmVzc2lvbiIsDQogIG1ldHJpYyA9ICJybXNlIiwNCiAgbGVhcm5pbmdfcmF0ZSA9IDAuMDEsDQogIGZlYXR1cmVfZnJhY3Rpb24gPSAxLA0KICBiYWdnaW5nX2ZyYWN0aW9uID0gMSwNCiAgbWF4X2RlcHRoID0gLTENCikNCg0KIyBUcmFpbiBtb2RlbA0KbW9kZWxvIDwtIGxnYi50cmFpbihwYXJhbXMgPSBteVBhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFUcmFpbl9sZ2JtLA0KICAgICAgICAgICAgICAgICAgICBucm91bmRzID0gMzAwMDAsDQogICAgICAgICAgICAgICAgICAgIHZhbGlkcyA9IGxpc3QodGVzdCA9IGRhdGFUZXN0X2xnYm0pLA0KICAgICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MDApDQoNCiNiZXN0IGl0ZXI6IDE5NTE5DQojYmVzdCBzY29yZTogMTAyLjE5ODgNCg0KIyBQcmVkaWN0aW9ucw0KcHJlZGljY2lvbmVzIDwtIHByZWRpY3QobW9kZWxvLCBkYXRhLm1hdHJpeChuZXdEYXRhVGVzdCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSwgZmVjaGFFc3BlY2lhbCkpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBudW1faXRlcmF0aW9uID0gbW9kZWxvJGJlc3RfaXRlcikNCg0KcHJlZGljY2lvbmVzW3ByZWRpY2Npb25lcyA8IDBdIDwtIDANCngxMSgpO2hpc3QocHJlZGljY2lvbmVzKQ0KDQojIFN1Ym1pc3Npb24NCmRhdGFTYW1wbGUgJT4lIA0KICBzZWxlY3QoSUQpICU+JSANCiAgbXV0YXRlKHRhcmdldCA9IHByZWRpY2Npb25lcykgLT4NCiAgbGdibVI5DQoNCiMgRXhwb3J0IHN1Ym1pc3Npb24gZm9yIHppbmRpDQp3cml0ZS5jc3YobGdibVI5LCBmaWxlID0gIi4uL3N1Ym1pc3Npb24vbGdibVI5VnVlbG9zLmNzdiIsDQogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpDQoNCmBgYA0KDQojIyBNb2RlbG8gMTANCg0KYGBge3J9DQojIE1vZDEgKyBLRm9sZA0KIyBMb2FkIGRhdGENCmxvYWQoIi4uL215RGF0YS9UcmFpbjEuUmRhdGEiKQ0KbG9hZCgiLi4vbXlEYXRhL1Rlc3QxLlJkYXRhIikNCmRhdGFTYW1wbGUgPC0gZGF0YS50YWJsZTo6ZnJlYWQoIi4uL2RhdGEvc2FtcGxlLmNzdiIpDQoNCm5ld0RhdGFUcmFpbiA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUcmFpbikNCm5ld0RhdGFUZXN0IDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRlc3QpDQoNCiMgU2VsZWN0aW9uIG9mIHZhcmlhYmxlcyBmb3IgYW5hbHlzaXMgDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmRhdGFUcmFpbiA8LSBuZXdEYXRhVHJhaW4gJT4lDQogIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkNCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBjYXJldCBmb3IgcGFydGl0aW9uIGRhdGEgd2l0aCByZXNhbXBsZQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRhdGFUcmFpbiR0YXJnZXQsIHRpbWVzID0gMTAsIHAgPSAwLjcwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gVFJVRSkNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4xLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMSwNCiAgYmFnZ2luZ19mcmFjdGlvbiA9IDEsDQogIG1heF9kZXB0aCA9IC0xDQopDQoNCiMgbGlnaHRnYm0gd2l0aCBLLUZvbGQgbWFudWFsbHkgKGsgPSAxMCkNCmxpYnJhcnkobGlnaHRnYm0pDQprIDwtIDEwDQpwcmVkVGVzdCA8LSBsaXN0KCkNCmJlc3RTY29yZSA8LSBjKCkNCmZvciAoaSBpbiAxOmspIHsNCiAgDQogICMgRGF0YSB0cmFpbiBhbmQgdGVzdA0KICBkYXRhVHJhaW5fbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGF0YVRyYWluW2luZGV4W1tpXV0sIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGF0YVRyYWluW2luZGV4W1tpXV0sIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQogIGRhdGFUZXN0X2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRhdGFUcmFpblstaW5kZXhbW2ldXSwgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRhdGFUcmFpblstaW5kZXhbW2ldXSwgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KICANCiAgIyBUcmFpbiBtb2RlbA0KICBtb2RlbCA8LSBsZ2IudHJhaW4ocGFyYW1zID0gbXlQYXJhbXMsDQogICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YVRyYWluX2xnYm0sDQogICAgICAgICAgICAgICAgICAgICBucm91bmRzID0gMTAwMDAsDQogICAgICAgICAgICAgICAgICAgICB2YWxpZHMgPSBsaXN0KHRlc3QgPSBkYXRhVGVzdF9sZ2JtKSwNCiAgICAgICAgICAgICAgICAgICAgIGVhcmx5X3N0b3BwaW5nX3JvdW5kcyA9IDUwMCkNCiAgDQogICMgUHJlZGljdGlvbnMNCiAgcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhLm1hdHJpeChuZXdEYXRhVGVzdCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG51bV9pdGVyYXRpb24gPSBtb2RlbCRiZXN0X2l0ZXIpDQogIHByZWRUZXN0W1tpXV0gPSBwcmVkaWN0aW9ucw0KICANCiAgIyBCZXN0IHNjb3JlIGZvciBpdGVyYXRpb24NCiAgYmVzdFNjb3JlW2ldID0gbW9kZWwkYmVzdF9zY29yZQ0KICANCiAgIyBOZXh0IGl0ZXJhdGlvbg0KICBjYXQoIkl0ZXJhdGlvbjo9PT09PT09PT09IiwgaSwgIlJTTUU6PT09PT09PT09PSIsIG1vZGVsJGJlc3Rfc2NvcmUsICJSZWFkeSEiKQ0KfQ0KDQojIE1lYW4gcHJlZGljdGlvbnMNCmRhdGFQcmVkIDwtIGFzLmRhdGEuZnJhbWUocHJlZFRlc3QpDQpuYW1lcyhkYXRhUHJlZCkgPC0gcGFzdGUwKCJNb2QiLCAxOjEwKQ0KcHJlZGljY2lvbmVzIDwtIGFwcGx5KGRhdGFQcmVkLCAxLCBtZWFuKQ0KDQpwcmVkaWNjaW9uZXNbcHJlZGljY2lvbmVzIDwgMF0gPC0gMA0KeDExKCk7aGlzdChwcmVkaWNjaW9uZXMpDQoNCiMgU3VibWlzc2lvbg0KZGF0YVNhbXBsZSAlPiUgDQogIHNlbGVjdChJRCkgJT4lIA0KICBtdXRhdGUodGFyZ2V0ID0gcHJlZGljY2lvbmVzKSAtPg0KICBsZ2JtUjEwDQoNCiMgRXhwb3J0IHN1Ym1pc3Npb24gZm9yIHppbmRpDQp3cml0ZS5jc3YobGdibVIxMCwgZmlsZSA9ICIuLi9zdWJtaXNzaW9uL2xnYm1SMTBWdWVsb3MuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQoNCmBgYA0KDQojIyBNb2RlbG8gMTENCg0KYGBge3J9DQojIE1vZDIgKyBLRm9sZA0KIyBMb2FkIGRhdGENCmxvYWQoIi4uL215RGF0YS9UcmFpbjEuUmRhdGEiKQ0KbG9hZCgiLi4vbXlEYXRhL1Rlc3QxLlJkYXRhIikNCmRhdGFTYW1wbGUgPC0gZGF0YS50YWJsZTo6ZnJlYWQoIi4uL2RhdGEvc2FtcGxlLmNzdiIpDQoNCm5ld0RhdGFUcmFpbiA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUcmFpbikNCm5ld0RhdGFUZXN0IDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRlc3QpDQoNCiMgU2VsZWN0aW9uIG9mIHZhcmlhYmxlcyBmb3IgYW5hbHlzaXMgDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmRhdGFUcmFpbiA8LSBuZXdEYXRhVHJhaW4gJT4lDQogIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkNCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBjYXJldCBmb3IgcGFydGl0aW9uIGRhdGEgd2l0aCByZXNhbXBsZQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRhdGFUcmFpbiR0YXJnZXQsIHRpbWVzID0gMTAsIHAgPSAwLjcwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gVFJVRSkNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4xLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMC41LA0KICBiYWdnaW5nX2ZyYWN0aW9uID0gMSwNCiAgbWluX2RhdGFfaW5fbGVhZiA9IDEwMCwNCiAgbnVtX2xlYXZlcyA9IDI1NSwNCiAgbWF4X2RlcHRoID0gLTENCikNCg0KIyBsaWdodGdibSB3aXRoIEstRm9sZCBtYW51YWxseSAoayA9IDEwKQ0KbGlicmFyeShsaWdodGdibSkNCmsgPC0gMTANCnByZWRUZXN0IDwtIGxpc3QoKQ0KYmVzdFNjb3JlIDwtIGMoKQ0KZm9yIChpIGluIDE6aykgew0KICANCiAgIyBEYXRhIHRyYWluIGFuZCB0ZXN0DQogIGRhdGFUcmFpbl9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkYXRhVHJhaW5baW5kZXhbW2ldXSwgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkYXRhVHJhaW5baW5kZXhbW2ldXSwgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCiAgZGF0YVRlc3RfbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGF0YVRyYWluWy1pbmRleFtbaV1dLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGF0YVRyYWluWy1pbmRleFtbaV1dLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQogIA0KICAjIFRyYWluIG1vZGVsDQogIG1vZGVsIDwtIGxnYi50cmFpbihwYXJhbXMgPSBteVBhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhVHJhaW5fbGdibSwNCiAgICAgICAgICAgICAgICAgICAgIG5yb3VuZHMgPSAxMDAwMCwNCiAgICAgICAgICAgICAgICAgICAgIHZhbGlkcyA9IGxpc3QodGVzdCA9IGRhdGFUZXN0X2xnYm0pLA0KICAgICAgICAgICAgICAgICAgICAgZWFybHlfc3RvcHBpbmdfcm91bmRzID0gNTAwKQ0KICANCiAgIyBQcmVkaWN0aW9ucw0KICBwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG1vZGVsLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEubWF0cml4KG5ld0RhdGFUZXN0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2l0ZXJhdGlvbiA9IG1vZGVsJGJlc3RfaXRlcikNCiAgcHJlZFRlc3RbW2ldXSA9IHByZWRpY3Rpb25zDQogIA0KICAjIEJlc3Qgc2NvcmUgZm9yIGl0ZXJhdGlvbg0KICBiZXN0U2NvcmVbaV0gPSBtb2RlbCRiZXN0X3Njb3JlDQogIA0KICAjIE5leHQgaXRlcmF0aW9uDQogIGNhdCgiSXRlcmF0aW9uOj09PT09PT09PT0iLCBpLCAiUlNNRTo9PT09PT09PT09IiwgbW9kZWwkYmVzdF9zY29yZSwgIlJlYWR5ISIpDQp9DQoNCiMgTWVhbiBwcmVkaWN0aW9ucw0KZGF0YVByZWQgPC0gYXMuZGF0YS5mcmFtZShwcmVkVGVzdCkNCm5hbWVzKGRhdGFQcmVkKSA8LSBwYXN0ZTAoIk1vZCIsIDE6MTApDQpwcmVkaWNjaW9uZXMgPC0gYXBwbHkoZGF0YVByZWQsIDEsIG1lYW4pDQoNCnByZWRpY2Npb25lc1twcmVkaWNjaW9uZXMgPCAwXSA8LSAwDQp4MTEoKTtoaXN0KHByZWRpY2Npb25lcykNCg0KIyBTdWJtaXNzaW9uDQpkYXRhU2FtcGxlICU+JSANCiAgc2VsZWN0KElEKSAlPiUgDQogIG11dGF0ZSh0YXJnZXQgPSBwcmVkaWNjaW9uZXMpIC0+DQogIGxnYm1SMTENCg0KIyBFeHBvcnQgc3VibWlzc2lvbiBmb3IgemluZGkNCndyaXRlLmNzdihsZ2JtUjExLCBmaWxlID0gIi4uL3N1Ym1pc3Npb24vbGdibVIxMVZ1ZWxvcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBNb2RlbG8gMTINCg0KYGBge3J9DQojIE1vZDEgKyBLRm9sZA0KIyBMb2FkIGRhdGENCmxvYWQoIi4uL215RGF0YS9UcmFpbjIuUmRhdGEiKQ0KbG9hZCgiLi4vbXlEYXRhL1Rlc3QyLlJkYXRhIikNCmRhdGFTYW1wbGUgPC0gZGF0YS50YWJsZTo6ZnJlYWQoIi4uL2RhdGEvc2FtcGxlLmNzdiIpDQoNCm5ld0RhdGFUcmFpbiA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUcmFpbikNCm5ld0RhdGFUZXN0IDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRlc3QpDQoNCiMgU2VsZWN0aW9uIG9mIHZhcmlhYmxlcyBmb3IgYW5hbHlzaXMgDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmRhdGFUcmFpbiA8LSBuZXdEYXRhVHJhaW4gJT4lDQogIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkNCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBjYXJldCBmb3IgcGFydGl0aW9uIGRhdGEgd2l0aCByZXNhbXBsZQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRhdGFUcmFpbiR0YXJnZXQsIHRpbWVzID0gMTAsIHAgPSAwLjcwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gVFJVRSkNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4wMSwNCiAgZmVhdHVyZV9mcmFjdGlvbiA9IDEsDQogIGJhZ2dpbmdfZnJhY3Rpb24gPSAxLA0KICBtYXhfZGVwdGggPSAtMQ0KKQ0KDQojIGxpZ2h0Z2JtIHdpdGggSy1Gb2xkIG1hbnVhbGx5IChrID0gMTApDQpsaWJyYXJ5KGxpZ2h0Z2JtKQ0KayA8LSAxMA0KcHJlZFRlc3QgPC0gbGlzdCgpDQpiZXN0U2NvcmUgPC0gYygpDQpmb3IgKGkgaW4gMTprKSB7DQogIA0KICAjIERhdGEgdHJhaW4gYW5kIHRlc3QNCiAgZGF0YVRyYWluX2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRhdGFUcmFpbltpbmRleFtbaV1dLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRhdGFUcmFpbltpbmRleFtbaV1dLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KICBkYXRhVGVzdF9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkYXRhVHJhaW5bLWluZGV4W1tpXV0sIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkYXRhVHJhaW5bLWluZGV4W1tpXV0sIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCiAgDQogICMgVHJhaW4gbW9kZWwNCiAgbW9kZWwgPC0gbGdiLnRyYWluKHBhcmFtcyA9IG15UGFyYW1zLA0KICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFUcmFpbl9sZ2JtLA0KICAgICAgICAgICAgICAgICAgICAgbnJvdW5kcyA9IDEwMDAwLA0KICAgICAgICAgICAgICAgICAgICAgdmFsaWRzID0gbGlzdCh0ZXN0ID0gZGF0YVRlc3RfbGdibSksDQogICAgICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MDApDQogIA0KICAjIFByZWRpY3Rpb25zDQogIHByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgZGF0YS5tYXRyaXgobmV3RGF0YVRlc3QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBudW1faXRlcmF0aW9uID0gbW9kZWwkYmVzdF9pdGVyKQ0KICBwcmVkVGVzdFtbaV1dID0gcHJlZGljdGlvbnMNCiAgDQogICMgQmVzdCBzY29yZSBmb3IgaXRlcmF0aW9uDQogIGJlc3RTY29yZVtpXSA9IG1vZGVsJGJlc3Rfc2NvcmUNCiAgDQogICMgTmV4dCBpdGVyYXRpb24NCiAgY2F0KCJJdGVyYXRpb246PT09PT09PT09PSIsIGksICJSU01FOj09PT09PT09PT0iLCBtb2RlbCRiZXN0X3Njb3JlLCAiUmVhZHkhIikNCn0NCg0KIyBNZWFuIHByZWRpY3Rpb25zDQpkYXRhUHJlZCA8LSBhcy5kYXRhLmZyYW1lKHByZWRUZXN0KQ0KbmFtZXMoZGF0YVByZWQpIDwtIHBhc3RlMCgiTW9kIiwgMToxMCkNCnByZWRpY2Npb25lcyA8LSBhcHBseShkYXRhUHJlZCwgMSwgbWVhbikNCg0KcHJlZGljY2lvbmVzW3ByZWRpY2Npb25lcyA8IDBdIDwtIDANCngxMSgpO2hpc3QocHJlZGljY2lvbmVzKQ0KDQojIFN1Ym1pc3Npb24NCmRhdGFTYW1wbGUgJT4lIA0KICBzZWxlY3QoSUQpICU+JSANCiAgbXV0YXRlKHRhcmdldCA9IHByZWRpY2Npb25lcykgLT4NCiAgbGdibVIxMg0KDQojIEV4cG9ydCBzdWJtaXNzaW9uIGZvciB6aW5kaQ0Kd3JpdGUuY3N2KGxnYm1SMTIsIGZpbGUgPSAiLi4vc3VibWlzc2lvbi9sZ2JtUjEyVnVlbG9zLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCg==